use crate::error::{IndicError, ParseError, ShapingError};
use crate::gpos::{self, Info};
use crate::gsub::{self, GlyphData, GlyphOrigin, GsubFeatureMask, RawGlyph};
use crate::layout::{GDEFTable, LangSys, LayoutCache, LayoutTable, GPOS, GSUB};
use crate::tinyvec::tiny_vec;
use crate::{tag, DOTTED_CIRCLE};
use log::debug;
use core::cmp;
use alloc::vec::Vec;
use unicode_general_category::GeneralCategory;
#[derive(Copy, Clone, Debug, PartialEq)]
enum Script {
Devanagari,
Bengali,
Gurmukhi,
Gujarati,
Oriya,
Tamil,
Telugu,
Kannada,
Malayalam,
Sinhala,
}
#[derive(Copy, Clone, Debug)]
enum BasePos {
Last,
LastSinhala,
}
#[derive(Copy, Clone, Debug)]
enum RephMode {
Explicit,
Implicit,
LogicalRepha,
}
#[derive(Copy, Clone, Debug, PartialEq)]
enum BlwfMode {
PostOnly,
PreAndPost,
}
impl Script {
fn base_consonant_pos(self) -> BasePos {
match self {
Script::Devanagari => BasePos::Last,
Script::Bengali => BasePos::Last,
Script::Gurmukhi => BasePos::Last,
Script::Gujarati => BasePos::Last,
Script::Oriya => BasePos::Last,
Script::Tamil => BasePos::Last,
Script::Telugu => BasePos::Last,
Script::Kannada => BasePos::Last,
Script::Malayalam => BasePos::Last,
Script::Sinhala => BasePos::LastSinhala,
}
}
fn reph_position(self) -> Pos {
match self {
Script::Devanagari => Pos::BeforePost,
Script::Bengali => Pos::AfterSubjoined,
Script::Gurmukhi => Pos::BeforeSubjoined,
Script::Gujarati => Pos::BeforePost,
Script::Oriya => Pos::AfterMain,
Script::Tamil => Pos::AfterPost,
Script::Telugu => Pos::AfterPost,
Script::Kannada => Pos::AfterPost,
Script::Malayalam => Pos::AfterMain,
Script::Sinhala => Pos::AfterMain,
}
}
fn reph_mode(self) -> RephMode {
match self {
Script::Devanagari => RephMode::Implicit,
Script::Bengali => RephMode::Implicit,
Script::Gurmukhi => RephMode::Implicit,
Script::Gujarati => RephMode::Implicit,
Script::Oriya => RephMode::Implicit,
Script::Tamil => RephMode::Implicit,
Script::Telugu => RephMode::Explicit,
Script::Kannada => RephMode::Implicit,
Script::Malayalam => RephMode::LogicalRepha,
Script::Sinhala => RephMode::Explicit,
}
}
fn blwf_mode(self) -> BlwfMode {
match self {
Script::Devanagari => BlwfMode::PreAndPost,
Script::Bengali => BlwfMode::PreAndPost,
Script::Gurmukhi => BlwfMode::PreAndPost,
Script::Gujarati => BlwfMode::PreAndPost,
Script::Oriya => BlwfMode::PreAndPost,
Script::Tamil => BlwfMode::PreAndPost,
Script::Telugu => BlwfMode::PostOnly,
Script::Kannada => BlwfMode::PostOnly,
Script::Malayalam => BlwfMode::PreAndPost,
Script::Sinhala => BlwfMode::PreAndPost,
}
}
fn abovebase_matra_pos(self) -> Option<Pos> {
match self {
Script::Devanagari => Some(Pos::AfterSubjoined),
Script::Bengali => None,
Script::Gurmukhi => Some(Pos::AfterPost),
Script::Gujarati => Some(Pos::AfterSubjoined),
Script::Oriya => Some(Pos::AfterMain),
Script::Tamil => Some(Pos::AfterSubjoined),
Script::Telugu => Some(Pos::BeforeSubjoined),
Script::Kannada => Some(Pos::BeforeSubjoined),
Script::Malayalam => None,
Script::Sinhala => Some(Pos::AfterSubjoined),
}
}
fn rightside_matra_pos(self, ch: char) -> Option<Pos> {
match self {
Script::Devanagari => Some(Pos::AfterSubjoined),
Script::Bengali => Some(Pos::AfterPost),
Script::Gurmukhi => Some(Pos::AfterPost),
Script::Gujarati => Some(Pos::AfterPost),
Script::Oriya => Some(Pos::AfterPost),
Script::Tamil => Some(Pos::AfterPost),
Script::Telugu => match ch {
'\u{0C41}' => Some(Pos::BeforeSubjoined),
'\u{0C42}' => Some(Pos::BeforeSubjoined),
'\u{0C43}' => Some(Pos::AfterSubjoined),
'\u{0C44}' => Some(Pos::AfterSubjoined),
_ => None,
},
Script::Kannada => match ch {
'\u{0CBE}' => Some(Pos::BeforeSubjoined),
'\u{0CC0}' => Some(Pos::BeforeSubjoined),
'\u{0CC1}' => Some(Pos::BeforeSubjoined),
'\u{0CC2}' => Some(Pos::BeforeSubjoined),
'\u{0CC3}' => Some(Pos::AfterSubjoined),
'\u{0CC4}' => Some(Pos::AfterSubjoined),
'\u{0CD5}' => Some(Pos::AfterSubjoined),
'\u{0CD6}' => Some(Pos::AfterSubjoined),
_ => None,
},
Script::Malayalam => Some(Pos::AfterPost),
Script::Sinhala => Some(Pos::AfterSubjoined),
}
}
fn belowbase_matra_pos(self) -> Pos {
match self {
Script::Devanagari => Pos::AfterSubjoined,
Script::Bengali => Pos::AfterSubjoined,
Script::Gurmukhi => Pos::AfterPost,
Script::Gujarati => Pos::AfterPost,
Script::Oriya => Pos::AfterSubjoined,
Script::Tamil => Pos::AfterPost,
Script::Telugu => Pos::BeforeSubjoined,
Script::Kannada => Pos::BeforeSubjoined,
Script::Malayalam => Pos::AfterPost,
Script::Sinhala => Pos::AfterSubjoined,
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
enum ShapingModel {
Indic1,
Indic2,
}
#[derive(Copy, Clone, Debug, PartialEq)]
enum BasicFeature {
Locl,
Nukt,
Akhn,
Rphf,
Rkrf,
Pref,
Blwf,
Abvf,
Half,
Pstf,
Vatu,
Cjct,
Cfar,
}
impl BasicFeature {
const ALL: &'static [BasicFeature] = &[
BasicFeature::Locl,
BasicFeature::Nukt,
BasicFeature::Akhn,
BasicFeature::Rphf,
BasicFeature::Rkrf,
BasicFeature::Pref,
BasicFeature::Blwf,
BasicFeature::Abvf,
BasicFeature::Half,
BasicFeature::Pstf,
BasicFeature::Vatu,
BasicFeature::Cjct,
BasicFeature::Cfar,
];
fn tag(self) -> u32 {
match self {
BasicFeature::Locl => tag::LOCL,
BasicFeature::Nukt => tag::NUKT,
BasicFeature::Akhn => tag::AKHN,
BasicFeature::Rphf => tag::RPHF,
BasicFeature::Rkrf => tag::RKRF,
BasicFeature::Pref => tag::PREF,
BasicFeature::Blwf => tag::BLWF,
BasicFeature::Abvf => tag::ABVF,
BasicFeature::Half => tag::HALF,
BasicFeature::Pstf => tag::PSTF,
BasicFeature::Vatu => tag::VATU,
BasicFeature::Cjct => tag::CJCT,
BasicFeature::Cfar => tag::CFAR,
}
}
fn mask(self) -> GsubFeatureMask {
match self {
BasicFeature::Locl => GsubFeatureMask::LOCL,
BasicFeature::Nukt => GsubFeatureMask::NUKT,
BasicFeature::Akhn => GsubFeatureMask::AKHN,
BasicFeature::Rphf => GsubFeatureMask::RPHF,
BasicFeature::Rkrf => GsubFeatureMask::RKRF,
BasicFeature::Pref => GsubFeatureMask::PREF,
BasicFeature::Blwf => GsubFeatureMask::BLWF,
BasicFeature::Abvf => GsubFeatureMask::ABVF,
BasicFeature::Half => GsubFeatureMask::HALF,
BasicFeature::Pstf => GsubFeatureMask::PSTF,
BasicFeature::Vatu => GsubFeatureMask::VATU,
BasicFeature::Cjct => GsubFeatureMask::CJCT,
BasicFeature::Cfar => GsubFeatureMask::CFAR,
}
}
fn is_global(self) -> bool {
match self {
BasicFeature::Locl => true,
BasicFeature::Nukt => true,
BasicFeature::Akhn => true,
BasicFeature::Rphf => false,
BasicFeature::Rkrf => true,
BasicFeature::Pref => false,
BasicFeature::Blwf => false,
BasicFeature::Abvf => true,
BasicFeature::Half => false,
BasicFeature::Pstf => false,
BasicFeature::Vatu => true,
BasicFeature::Cjct => true,
BasicFeature::Cfar => true,
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
enum ShapingClass {
Bindu,
Visarga,
Avagraha,
Nukta,
Virama,
Cantillation,
GeminationMark,
PureKiller,
SyllableModifier,
Consonant,
VowelIndependent,
VowelDependent,
ConsonantDead,
ConsonantMedial,
ConsonantPlaceholder,
ConsonantWithStacker,
ConsonantPreRepha,
ModifyingLetter,
Placeholder,
Number,
Symbol,
Joiner,
NonJoiner,
DottedCircle,
}
#[derive(Copy, Clone, Debug)]
enum MarkPlacementSubclass {
TopPosition,
RightPosition,
BottomPosition,
LeftPosition,
LeftAndRightPosition,
TopAndRightPosition,
TopAndLeftPosition,
TopLeftAndRightPosition,
TopAndBottomPosition,
Overstruck,
}
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord)]
enum Pos {
RaToBecomeReph,
PrebaseMatra,
PrebaseConsonant,
SyllableBase,
AfterMain,
_AbovebaseConsonant,
BeforeSubjoined,
BelowbaseConsonant,
AfterSubjoined,
BeforePost,
PostbaseConsonant,
AfterPost,
_FinalConsonant,
SMVD,
}
#[derive(Copy, Clone, Debug)]
enum Syllable {
Consonant,
Vowel,
Standalone,
Symbol,
Broken,
}
fn shaping_class(ch: char) -> Option<ShapingClass> {
let (shaping, _) = indic_character(ch);
shaping
}
fn consonant(ch: char) -> bool {
match shaping_class(ch) {
Some(ShapingClass::Consonant) => !ra(ch),
Some(ShapingClass::ConsonantDead) => true,
_ => false,
}
}
fn vowel(ch: char) -> bool {
match shaping_class(ch) {
Some(ShapingClass::VowelIndependent) => true,
_ => false,
}
}
fn nukta(ch: char) -> bool {
match shaping_class(ch) {
Some(ShapingClass::Nukta) => true,
_ => false,
}
}
fn halant(ch: char) -> bool {
match shaping_class(ch) {
Some(ShapingClass::Virama) => true,
_ => false,
}
}
fn zwj(ch: char) -> bool {
match shaping_class(ch) {
Some(ShapingClass::Joiner) => true,
_ => false,
}
}
fn zwnj(ch: char) -> bool {
match shaping_class(ch) {
Some(ShapingClass::NonJoiner) => true,
_ => false,
}
}
fn ra(ch: char) -> bool {
match ch {
'\u{0930}' => true,
'\u{09B0}' => true,
'\u{09F0}' => true,
'\u{0A30}' => true,
'\u{0AB0}' => true,
'\u{0B30}' => true,
'\u{0BB0}' => true,
'\u{0C30}' => true,
'\u{0CB0}' => true,
'\u{0D30}' => true,
'\u{0DBB}' => true,
_ => false,
}
}
fn matra(ch: char) -> bool {
match shaping_class(ch) {
Some(ShapingClass::VowelDependent) => true,
Some(ShapingClass::PureKiller) => true,
_ => false,
}
}
fn syllable_modifier(ch: char) -> bool {
match shaping_class(ch) {
Some(ShapingClass::SyllableModifier) => true,
Some(ShapingClass::Bindu) => true,
Some(ShapingClass::Visarga) => true,
Some(ShapingClass::GeminationMark) => true,
_ => false,
}
}
fn vedic_sign(ch: char) -> bool {
match shaping_class(ch) {
Some(ShapingClass::Cantillation) => true,
_ => false,
}
}
fn placeholder(ch: char) -> bool {
match shaping_class(ch) {
Some(ShapingClass::Number) => true,
Some(ShapingClass::Placeholder) => true,
Some(ShapingClass::ConsonantPlaceholder) => true,
_ => false,
}
}
fn dotted_circle(ch: char) -> bool {
match shaping_class(ch) {
Some(ShapingClass::DottedCircle) => true,
_ => false,
}
}
fn repha(ch: char) -> bool {
match shaping_class(ch) {
Some(ShapingClass::ConsonantPreRepha) => true,
_ => false,
}
}
fn consonant_medial(ch: char) -> bool {
match shaping_class(ch) {
Some(ShapingClass::ConsonantMedial) => true,
_ => false,
}
}
fn symbol(ch: char) -> bool {
match shaping_class(ch) {
Some(ShapingClass::Symbol) => true,
Some(ShapingClass::Avagraha) => true,
_ => false,
}
}
fn consonant_with_stacker(ch: char) -> bool {
match shaping_class(ch) {
Some(ShapingClass::ConsonantWithStacker) => true,
_ => false,
}
}
#[allow(dead_code)]
fn other(ch: char) -> bool {
match shaping_class(ch) {
Some(ShapingClass::ModifyingLetter) => true,
_ => false,
}
}
fn match_unit<T: IndicChar>(_cs: &[T]) -> Option<usize> {
Some(0)
}
fn match_one<T: IndicChar>(cs: &[T], f: impl FnOnce(char) -> bool) -> Option<usize> {
if !cs.is_empty() && f(cs[0].ch()) {
Some(1)
} else {
None
}
}
fn match_nonempty<T: IndicChar>(cs: &[T], f: impl FnOnce(&[T]) -> Option<usize>) -> Option<usize> {
match f(cs) {
Some(n) if n > 0 => Some(n),
_ => None,
}
}
fn match_optional<T: IndicChar>(cs: &[T], f: impl FnOnce(&[T]) -> Option<usize>) -> Option<usize> {
Some(f(cs).unwrap_or(0))
}
fn match_optional_seq<T: IndicChar>(
cs: &[T],
f: impl FnOnce(&[T]) -> Option<usize>,
g: impl Copy + Fn(&[T]) -> Option<usize>,
) -> Option<usize> {
match_either(cs, g, |cs| match_seq(cs, f, g))
}
fn match_repeat_num<T: IndicChar>(
mut cs: &[T],
num: usize,
f: &impl Fn(&[T]) -> Option<usize>,
) -> Option<usize> {
let mut total: usize = 0;
for _i in 0..num {
let n = f(cs)?;
total += n;
cs = &cs[n..];
}
Some(total)
}
fn match_repeat_upto<T: IndicChar>(
cs: &[T],
max: usize,
f: impl Fn(&[T]) -> Option<usize>,
g: impl Fn(&[T]) -> Option<usize>,
) -> Option<usize> {
let mut best = None;
for i in 0..=max {
if let Some(nf) = match_repeat_num(cs, i, &f) {
if let Some(ng) = g(&cs[nf..]) {
best = Some(nf + ng)
}
}
}
best
}
fn match_seq<T: IndicChar>(
cs: &[T],
f1: impl FnOnce(&[T]) -> Option<usize>,
f2: impl FnOnce(&[T]) -> Option<usize>,
) -> Option<usize> {
let n1 = f1(cs)?;
let n2 = f2(&cs[n1..])?;
Some(n1 + n2)
}
fn match_either<T: IndicChar>(
cs: &[T],
f1: impl FnOnce(&[T]) -> Option<usize>,
f2: impl FnOnce(&[T]) -> Option<usize>,
) -> Option<usize> {
let res1 = f1(cs);
let res2 = f2(cs);
match (res1, res2) {
(Some(n1), Some(n2)) => Some(cmp::max(n1, n2)),
(Some(n1), None) => Some(n1),
(None, Some(n2)) => Some(n2),
(None, None) => None,
}
}
fn match_either_seq<T: IndicChar>(
cs: &[T],
f1: impl FnOnce(&[T]) -> Option<usize>,
f2: impl FnOnce(&[T]) -> Option<usize>,
g: impl Copy + Fn(&[T]) -> Option<usize>,
) -> Option<usize> {
let res1 = match_seq(cs, f1, &g);
let res2 = match_seq(cs, f2, &g);
match (res1, res2) {
(Some(n1), Some(n2)) => Some(cmp::max(n1, n2)),
(Some(n1), None) => Some(n1),
(None, Some(n2)) => Some(n2),
(None, None) => None,
}
}
fn match_c<T: IndicChar>(cs: &[T]) -> Option<usize> {
match_either(cs, |cs| match_one(cs, consonant), |cs| match_one(cs, ra))
}
fn match_z<T: IndicChar>(cs: &[T]) -> Option<usize> {
match_either(cs, |cs| match_one(cs, zwj), |cs| match_one(cs, zwnj))
}
#[rustfmt::skip]
fn match_reph<T: IndicChar>(cs: &[T]) -> Option<usize> {
match_either(
cs,
|cs| match_seq(cs, |cs| match_one(cs, ra), |cs| match_one(cs, halant)),
|cs| match_one(cs, repha),
)
}
#[rustfmt::skip]
fn match_cn<T: IndicChar>(cs: &[T]) -> Option<usize> {
match_seq(cs,
match_c,
|cs| match_optional_seq(cs,
|cs| match_one(cs, zwj),
|cs| match_optional(cs, |cs| match_one(cs, nukta)),
)
)
}
#[rustfmt::skip]
fn match_forced_rakar<T: IndicChar>(cs: &[T]) -> Option<usize> {
match_seq(
cs,
|cs| match_one(cs, zwj),
|cs| {
match_seq(
cs,
|cs| match_one(cs, halant),
|cs| match_seq(cs, |cs| match_one(cs, zwj), |cs| match_one(cs, ra)),
)
},
)
}
fn match_s<T: IndicChar>(cs: &[T]) -> Option<usize> {
match_seq(
cs,
|cs| match_one(cs, symbol),
|cs| match_optional(cs, |cs| match_one(cs, nukta)),
)
}
#[rustfmt::skip]
fn match_matra_group<T: IndicChar>(cs: &[T]) -> Option<usize> {
match_repeat_upto(cs, 3, match_z,
|cs| {
match_seq(
cs,
|cs| match_one(cs, matra),
|cs| match_optional_seq(cs,
|cs| match_one(cs, nukta),
|cs| {
match_optional(cs, |cs| {
match_either(cs, |cs| match_one(cs, halant), match_forced_rakar)
})
},
)
)
},
)
}
#[rustfmt::skip]
fn match_syllable_tail<T: IndicChar>(cs: &[T]) -> Option<usize> {
match_optional_seq(cs,
|cs| match_optional_seq(cs,
match_z,
|cs| match_seq(cs,
|cs| match_one(cs, syllable_modifier),
|cs| match_optional_seq(cs,
|cs| match_one(cs, syllable_modifier),
|cs| match_optional(cs, |cs| match_one(cs, zwnj)),
)
)
),
|cs| match_repeat_upto(cs, 3, |cs| match_one(cs, vedic_sign), match_unit),
)
}
#[rustfmt::skip]
fn match_halant_group<T: IndicChar>(cs: &[T]) -> Option<usize> {
match_optional_seq(cs,
match_z,
|cs| match_seq(cs,
|cs| match_one(cs, halant),
|cs| match_optional(cs,
|cs| match_seq(cs,
|cs| match_one(cs, zwj),
|cs| match_optional(cs, |cs| match_one(cs, nukta)),
)
)
)
)
}
fn match_medial_group<T: IndicChar>(cs: &[T]) -> Option<usize> {
match_optional(cs, |cs| match_one(cs, consonant_medial))
}
#[rustfmt::skip]
fn match_halant_or_matra_group<T: IndicChar>(cs: &[T]) -> Option<usize> {
match_either(cs,
|cs| match_seq(cs,
|cs| match_one(cs, halant),
|cs| match_one(cs, zwnj)),
|cs| match_either(cs,
|cs| match_repeat_upto(cs, 4, match_matra_group, match_unit),
match_halant_group,
)
)
}
#[rustfmt::skip]
fn match_consonant_syllable<T: IndicChar>(cs: &[T]) -> Option<usize> {
match_optional_seq(cs,
|cs| match_either(cs,
|cs| match_one(cs, repha),
|cs| match_one(cs, consonant_with_stacker),
),
|cs| match_repeat_upto(cs, 4,
|cs| match_seq(cs,
match_cn,
match_halant_group
),
|cs| match_seq(cs, match_cn,
|cs| match_seq(cs,
match_medial_group,
|cs| match_seq(cs,
match_halant_or_matra_group,
match_syllable_tail
)
)
)
)
)
}
#[rustfmt::skip]
fn match_vowel_syllable<T: IndicChar>(cs: &[T]) -> Option<usize> {
match_optional_seq(cs,
match_reph,
|cs| match_seq(cs,
|cs| match_one(cs, vowel),
|cs| match_optional_seq(cs,
|cs| match_one(cs, nukta),
|cs| match_either(cs,
|cs| match_one(cs, zwj),
|cs| match_repeat_upto(cs, 4,
|cs| match_seq(cs,
match_halant_group,
match_cn),
|cs| match_seq(cs,
match_medial_group,
|cs| match_seq(cs,
match_halant_or_matra_group,
match_syllable_tail,
)
)
)
)
)
)
)
}
#[rustfmt::skip]
fn match_standalone_syllable<T: IndicChar>(cs: &[T]) -> Option<usize> {
match_either_seq(cs,
|cs| match_optional_seq(cs,
|cs| match_either(cs,
|cs| match_one(cs, repha),
|cs| match_one(cs, consonant_with_stacker),
),
|cs| match_one(cs, placeholder)
),
|cs| match_seq(cs,
|cs| match_optional(cs, match_reph),
|cs| match_one(cs, dotted_circle),
),
|cs| match_optional_seq(cs,
|cs| match_one(cs, nukta),
|cs| match_repeat_upto(cs, 4,
|cs| match_seq(cs,
match_halant_group,
match_cn
),
|cs| match_seq(cs,
match_medial_group,
|cs| match_seq(cs,
match_halant_or_matra_group,
match_syllable_tail
)
)
)
)
)
}
fn match_symbol_syllable<T: IndicChar>(cs: &[T]) -> Option<usize> {
match_seq(cs, match_s, match_syllable_tail)
}
#[rustfmt::skip]
fn match_broken_syllable<T: IndicChar>(cs: &[T]) -> Option<usize> {
match_nonempty(cs,
|cs| match_optional_seq(cs,
match_reph,
|cs| match_optional_seq(cs,
|cs| match_one(cs, nukta),
|cs| match_repeat_upto(cs, 4,
|cs| match_seq(cs,
match_halant_group,
match_cn
),
|cs| match_seq(cs,
match_medial_group,
|cs| match_seq(cs,
match_halant_or_matra_group,
match_syllable_tail
)
)
)
)
)
)
}
fn match_syllable<T: IndicChar>(cs: &[T]) -> Option<(usize, Syllable)> {
let consonant = (match_consonant_syllable(cs), Syllable::Consonant);
let vowel = (match_vowel_syllable(cs), Syllable::Vowel);
let standalone = (match_standalone_syllable(cs), Syllable::Standalone);
let symbol = (match_symbol_syllable(cs), Syllable::Symbol);
let broken = (match_broken_syllable(cs), Syllable::Broken);
let syllables = &mut [consonant, vowel, standalone, symbol, broken];
syllables.sort_by(|(len1, _), (len2, _)| len2.cmp(len1));
match syllables[0] {
(Some(len), syllable_type) => Some((len, syllable_type)),
(None, _) => None,
}
}
trait IndicChar {
fn ch(&self) -> char;
}
impl IndicChar for RawGlyph<()> {
fn ch(&self) -> char {
match self.glyph_origin {
GlyphOrigin::Char(ch) => ch,
GlyphOrigin::Direct => panic!("unexpected glyph origin"),
}
}
}
pub fn preprocess_indic(cs: &mut Vec<char>) {
constrain_vowel(cs);
decompose_matra(cs);
recompose_bengali_ya_nukta(cs);
reorder_marks(cs);
reorder_kannada_ra_halant_zwj(cs);
}
enum InsertConstraint {
Between,
MaybeAfter(char),
None,
}
fn constrain_vowel(cs: &mut Vec<char>) {
let mut i = 0;
while i + 1 < cs.len() {
i += match vowel_constraint(cs[i], cs[i + 1]) {
InsertConstraint::Between => {
cs.insert(i + 1, DOTTED_CIRCLE);
3
}
InsertConstraint::MaybeAfter(c3) => {
if i + 2 < cs.len() && cs[i + 2] == c3 {
cs.insert(i + 2, DOTTED_CIRCLE);
4
} else {
2
}
}
InsertConstraint::None => 1,
}
}
}
fn vowel_constraint(c1: char, c2: char) -> InsertConstraint {
match (c1, c2) {
('\u{0905}', '\u{0946}') => InsertConstraint::Between,
('\u{0905}', '\u{093E}') => InsertConstraint::Between,
('\u{0909}', '\u{0941}') => InsertConstraint::Between,
('\u{090F}', '\u{0945}') => InsertConstraint::Between,
('\u{090F}', '\u{0946}') => InsertConstraint::Between,
('\u{090F}', '\u{0947}') => InsertConstraint::Between,
('\u{0905}', '\u{0949}') => InsertConstraint::Between,
('\u{0906}', '\u{0945}') => InsertConstraint::Between,
('\u{0905}', '\u{094A}') => InsertConstraint::Between,
('\u{0906}', '\u{0946}') => InsertConstraint::Between,
('\u{0905}', '\u{094B}') => InsertConstraint::Between,
('\u{0906}', '\u{0947}') => InsertConstraint::Between,
('\u{0905}', '\u{094C}') => InsertConstraint::Between,
('\u{0906}', '\u{0948}') => InsertConstraint::Between,
('\u{0905}', '\u{0945}') => InsertConstraint::Between,
('\u{0905}', '\u{093A}') => InsertConstraint::Between,
('\u{0905}', '\u{093B}') => InsertConstraint::Between,
('\u{0906}', '\u{093A}') => InsertConstraint::Between,
('\u{0905}', '\u{094F}') => InsertConstraint::Between,
('\u{0905}', '\u{0956}') => InsertConstraint::Between,
('\u{0905}', '\u{0957}') => InsertConstraint::Between,
('\u{0930}', '\u{094D}') => InsertConstraint::MaybeAfter('\u{0907}'),
('\u{0985}', '\u{09BE}') => InsertConstraint::Between,
('\u{098B}', '\u{09C3}') => InsertConstraint::Between,
('\u{098C}', '\u{09E2}') => InsertConstraint::Between,
('\u{0A05}', '\u{0A3E}') => InsertConstraint::Between,
('\u{0A72}', '\u{0A3F}') => InsertConstraint::Between,
('\u{0A72}', '\u{0A40}') => InsertConstraint::Between,
('\u{0A73}', '\u{0A41}') => InsertConstraint::Between,
('\u{0A73}', '\u{0A42}') => InsertConstraint::Between,
('\u{0A72}', '\u{0A47}') => InsertConstraint::Between,
('\u{0A05}', '\u{0A48}') => InsertConstraint::Between,
('\u{0A73}', '\u{0A4B}') => InsertConstraint::Between,
('\u{0A05}', '\u{0A4C}') => InsertConstraint::Between,
('\u{0A85}', '\u{0ABE}') => InsertConstraint::Between,
('\u{0A85}', '\u{0AC5}') => InsertConstraint::Between,
('\u{0A85}', '\u{0AC7}') => InsertConstraint::Between,
('\u{0A85}', '\u{0AC8}') => InsertConstraint::Between,
('\u{0A85}', '\u{0AC9}') => InsertConstraint::Between,
('\u{0A85}', '\u{0ACB}') => InsertConstraint::Between,
('\u{0A85}', '\u{0ACC}') => InsertConstraint::Between,
('\u{0AC5}', '\u{0ABE}') => InsertConstraint::Between,
('\u{0B05}', '\u{0B3E}') => InsertConstraint::Between,
('\u{0B0F}', '\u{0B57}') => InsertConstraint::Between,
('\u{0B13}', '\u{0B57}') => InsertConstraint::Between,
('\u{0C12}', '\u{0C55}') => InsertConstraint::Between,
('\u{0C12}', '\u{0C4C}') => InsertConstraint::Between,
('\u{0C3F}', '\u{0C55}') => InsertConstraint::Between,
('\u{0C46}', '\u{0C55}') => InsertConstraint::Between,
('\u{0C4A}', '\u{0C55}') => InsertConstraint::Between,
('\u{0C89}', '\u{0CBE}') => InsertConstraint::Between,
('\u{0C92}', '\u{0CCC}') => InsertConstraint::Between,
('\u{0C8B}', '\u{0CBE}') => InsertConstraint::Between,
('\u{0D07}', '\u{0D57}') => InsertConstraint::Between,
('\u{0D09}', '\u{0D57}') => InsertConstraint::Between,
('\u{0D0E}', '\u{0D46}') => InsertConstraint::Between,
('\u{0D12}', '\u{0D3E}') => InsertConstraint::Between,
('\u{0D12}', '\u{0D57}') => InsertConstraint::Between,
('\u{0D85}', '\u{0DCF}') => InsertConstraint::Between,
('\u{0D85}', '\u{0DD0}') => InsertConstraint::Between,
('\u{0D85}', '\u{0DD1}') => InsertConstraint::Between,
('\u{0D8B}', '\u{0DDF}') => InsertConstraint::Between,
('\u{0D8D}', '\u{0DD8}') => InsertConstraint::Between,
('\u{0D8F}', '\u{0DDF}') => InsertConstraint::Between,
('\u{0D91}', '\u{0DCA}') => InsertConstraint::Between,
('\u{0D91}', '\u{0DD9}') => InsertConstraint::Between,
('\u{0D91}', '\u{0DDA}') => InsertConstraint::Between,
('\u{0D91}', '\u{0DDC}') => InsertConstraint::Between,
('\u{0D91}', '\u{0DDD}') => InsertConstraint::Between,
('\u{0D94}', '\u{0DDF}') => InsertConstraint::Between,
_ => InsertConstraint::None,
}
}
enum MatraSplit {
None,
Two(char, char),
Three(char, char, char),
}
fn decompose_matra(cs: &mut Vec<char>) {
let mut i = 0;
while i < cs.len() {
i += match split_matra(cs[i]) {
MatraSplit::None => 1,
MatraSplit::Two(c1, c2) => {
cs[i] = c1;
cs.insert(i + 1, c2);
2
}
MatraSplit::Three(c1, c2, c3) => {
cs[i] = c1;
cs.insert(i + 1, c2);
cs.insert(i + 2, c3);
3
}
}
}
}
fn split_matra(ch: char) -> MatraSplit {
match ch {
'\u{09CB}' => MatraSplit::Two('\u{09C7}', '\u{09BE}'),
'\u{09CC}' => MatraSplit::Two('\u{09C7}', '\u{09D7}'),
'\u{0B48}' => MatraSplit::Two('\u{0B47}', '\u{0B56}'),
'\u{0B4B}' => MatraSplit::Two('\u{0B47}', '\u{0B3E}'),
'\u{0B4C}' => MatraSplit::Two('\u{0B47}', '\u{0B57}'),
'\u{0BCA}' => MatraSplit::Two('\u{0BC6}', '\u{0BBE}'),
'\u{0BCB}' => MatraSplit::Two('\u{0BC7}', '\u{0BBE}'),
'\u{0BCC}' => MatraSplit::Two('\u{0BC6}', '\u{0BD7}'),
'\u{0C48}' => MatraSplit::Two('\u{0C46}', '\u{0C56}'),
'\u{0CC0}' => MatraSplit::Two('\u{0CBF}', '\u{0CD5}'),
'\u{0CC7}' => MatraSplit::Two('\u{0CC6}', '\u{0CD5}'),
'\u{0CC8}' => MatraSplit::Two('\u{0CC6}', '\u{0CD6}'),
'\u{0CCA}' => MatraSplit::Two('\u{0CC6}', '\u{0CC2}'),
'\u{0CCB}' => MatraSplit::Three('\u{0CC6}', '\u{0CC2}', '\u{0CD5}'),
'\u{0D4A}' => MatraSplit::Two('\u{0D46}', '\u{0D3E}'),
'\u{0D4B}' => MatraSplit::Two('\u{0D47}', '\u{0D3E}'),
'\u{0D4C}' => MatraSplit::Two('\u{0D46}', '\u{0D57}'),
'\u{0DDA}' => MatraSplit::Two('\u{0DD9}', '\u{0DCA}'),
'\u{0DDC}' => MatraSplit::Two('\u{0DD9}', '\u{0DCF}'),
'\u{0DDD}' => MatraSplit::Three('\u{0DD9}', '\u{0DCF}', '\u{0DCA}'),
'\u{0DDE}' => MatraSplit::Two('\u{0DD9}', '\u{0DDF}'),
_ => MatraSplit::None,
}
}
fn recompose_bengali_ya_nukta(cs: &mut Vec<char>) {
let mut i = 0;
while i + 1 < cs.len() {
if cs[i] == '\u{09AF}' && cs[i + 1] == '\u{09BC}' {
cs[i] = '\u{09DF}';
cs.remove(i + 1);
}
i += 1;
}
}
fn reorder_marks(cs: &mut [char]) {
use core::cmp::Ordering;
fn mark_order(mark1: &char, mark2: &char) -> Ordering {
if (nukta(*mark1) && halant(*mark2))
|| (halant(*mark1) && vedic_sign(*mark2))
|| (nukta(*mark1) && vedic_sign(*mark2))
{
Ordering::Less
} else {
Ordering::Equal
}
}
let iter = cs.split_mut(|&c| !(nukta(c) || halant(c) || vedic_sign(c)));
iter.for_each(|marks| marks.sort_by(mark_order));
}
fn reorder_kannada_ra_halant_zwj(cs: &mut [char]) {
if cs.starts_with(&['\u{0CB0}', '\u{0CCD}', '\u{200D}']) {
cs.swap(1, 2);
}
}
#[derive(Clone)]
struct IndicData {
pos: Option<Pos>,
mask: GsubFeatureMask,
}
impl GlyphData for IndicData {
fn merge(data1: IndicData, data2: IndicData) -> IndicData {
match (data1.pos, data2.pos) {
(Some(Pos::SyllableBase), _) => data1,
(_, Some(Pos::SyllableBase)) => data2,
(Some(Pos::PrebaseConsonant), _) => data1,
(_, Some(Pos::PrebaseConsonant)) => data2,
(Some(Pos::PostbaseConsonant), _) => data1,
(_, Some(Pos::PostbaseConsonant)) => data2,
(_, None) => data1,
(None, _) => data2,
_ => data1,
}
}
}
type RawGlyphIndic = RawGlyph<IndicData>;
impl RawGlyphIndic {
fn is(&self, pred: impl FnOnce(char) -> bool) -> bool {
match self.glyph_origin {
GlyphOrigin::Char(c) => pred(c),
GlyphOrigin::Direct => false,
}
}
fn has_pos(&self, pos: Pos) -> bool {
match self.extra_data.pos {
Some(p) => p == pos,
None => false,
}
}
fn set_pos(&mut self, pos: Option<Pos>) {
self.extra_data.pos = pos
}
fn replace_none_pos(&mut self, pos: Option<Pos>) {
assert_eq!(self.extra_data.pos, None);
self.set_pos(pos)
}
fn pos(&self) -> Option<Pos> {
self.extra_data.pos
}
fn has_mask(&self, mask: GsubFeatureMask) -> bool {
self.extra_data.mask.contains(mask)
}
fn add_mask(&mut self, mask: GsubFeatureMask) {
self.extra_data.mask.insert(mask)
}
fn remove_mask(&mut self, mask: GsubFeatureMask) {
self.extra_data.mask.remove(mask)
}
}
struct IndicShapingData<'tables> {
gsub_cache: &'tables LayoutCache<GSUB>,
gsub_table: &'tables LayoutTable<GSUB>,
gdef_table: Option<&'tables GDEFTable>,
langsys: &'tables LangSys,
script_tag: u32,
lang_tag: Option<u32>,
script: Script,
shaping_model: ShapingModel,
}
impl IndicShapingData<'_> {
fn feature_would_apply(
&self,
feature_tag: u32,
glyphs: &[RawGlyphIndic],
start_index: usize,
) -> Result<bool, ParseError> {
gsub::gsub_feature_would_apply(
self.gsub_cache,
self.gsub_table,
self.gdef_table,
self.langsys,
feature_tag,
glyphs,
start_index,
)
}
fn get_lookups_cache_index(&self, mask: GsubFeatureMask) -> Result<usize, ParseError> {
gsub::get_lookups_cache_index(self.gsub_cache, self.script_tag, self.lang_tag, mask)
}
fn apply_lookup(
&self,
lookup_index: usize,
feature_tag: u32,
glyphs: &mut Vec<RawGlyphIndic>,
pred: impl Fn(&RawGlyphIndic) -> bool,
) -> Result<(), ParseError> {
gsub::gsub_apply_lookup(
self.gsub_cache,
self.gsub_table,
self.gdef_table,
lookup_index,
feature_tag,
None,
glyphs,
0,
glyphs.len(),
pred,
)?;
Ok(())
}
}
pub fn gsub_apply_indic<'data>(
dotted_circle_index: u16,
gsub_cache: &LayoutCache<GSUB>,
gsub_table: &LayoutTable<GSUB>,
gdef_table: Option<&GDEFTable>,
indic1_tag: u32,
lang_tag: Option<u32>,
glyphs: &mut Vec<RawGlyph<()>>,
) -> Result<(), ShapingError> {
if glyphs.is_empty() {
return Err(IndicError::EmptyBuffer.into());
}
let indic2_tag = indic2_tag(indic1_tag);
let (script_tag, shaping_model, script_table) = match gsub_table.find_script(indic2_tag)? {
Some(script_table) => (indic2_tag, ShapingModel::Indic2, script_table),
None => match gsub_table.find_script_or_default(indic1_tag)? {
Some(script_table) => (indic1_tag, ShapingModel::Indic1, script_table),
None => return Ok(()),
},
};
let langsys = match script_table.find_langsys_or_default(lang_tag)? {
Some(langsys) => langsys,
None => return Ok(()),
};
let mut syllables = to_indic_syllables(glyphs);
let script = script(indic1_tag);
let shaping_data = IndicShapingData {
gsub_cache,
gsub_table,
gdef_table,
langsys: &langsys,
script_tag,
lang_tag,
script,
shaping_model,
};
for i in 0..syllables.len() {
let is_first_syllable = if i == 0 {
true
} else {
if let Some(prev_glyph) = syllables[i - 1].0.iter().last() {
match prev_glyph.glyph_origin {
GlyphOrigin::Char(c) => {
let gc = unicode_general_category::get_general_category(c);
!(gc == GeneralCategory::Format
|| gc == GeneralCategory::Unassigned
|| gc == GeneralCategory::PrivateUse
|| gc == GeneralCategory::Surrogate
|| gc == GeneralCategory::LowercaseLetter
|| gc == GeneralCategory::ModifierLetter
|| gc == GeneralCategory::OtherLetter
|| gc == GeneralCategory::TitlecaseLetter
|| gc == GeneralCategory::UppercaseLetter
|| gc == GeneralCategory::SpacingMark
|| gc == GeneralCategory::EnclosingMark
|| gc == GeneralCategory::NonspacingMark)
}
GlyphOrigin::Direct => false,
}
} else {
true
}
};
let (syllable, syllable_type) = &mut syllables[i];
if let Err(err) = shape_syllable(
dotted_circle_index,
&shaping_data,
syllable,
syllable_type,
is_first_syllable,
) {
debug!("gsub apply indic: {}", err);
}
}
*glyphs = syllables
.into_iter()
.flat_map(|(s, _)| s.into_iter())
.map(from_raw_glyph_indic)
.collect();
Ok(())
}
fn shape_syllable(
dotted_circle_index: u16,
shaping_data: &IndicShapingData<'_>,
syllable: &mut Vec<RawGlyphIndic>,
syllable_type: &Option<Syllable>,
is_first_syllable: bool,
) -> Result<(), ShapingError> {
if let Some(Syllable::Broken) = syllable_type {
insert_dotted_circle(dotted_circle_index, shaping_data.script, syllable)?;
}
match syllable_type {
Some(Syllable::Consonant)
| Some(Syllable::Vowel)
| Some(Syllable::Standalone)
| Some(Syllable::Broken) => {
initial_reorder_consonant_syllable(&shaping_data, syllable)?;
apply_basic_features(&shaping_data, syllable)?;
final_reorder_consonant_syllable(&shaping_data, syllable);
apply_presentation_features(&shaping_data, is_first_syllable, syllable)?;
}
Some(Syllable::Symbol) | None => {}
}
Ok(())
}
fn insert_dotted_circle(
dotted_circle_index: u16,
script: Script,
glyphs: &mut Vec<RawGlyphIndic>,
) -> Result<(), IndicError> {
if dotted_circle_index == 0 {
return Err(IndicError::MissingDottedCircle);
}
let dotted_circle = RawGlyphIndic {
unicodes: tiny_vec![[char; 1] => DOTTED_CIRCLE],
glyph_index: dotted_circle_index,
liga_component_pos: 0,
glyph_origin: GlyphOrigin::Char(DOTTED_CIRCLE),
small_caps: false,
multi_subst_dup: false,
is_vert_alt: false,
fake_bold: false,
fake_italic: false,
variation: None,
extra_data: IndicData {
pos: None,
mask: GsubFeatureMask::empty(),
},
};
let mut pos = 0;
if let (Script::Malayalam, Some(glyph)) = (script, glyphs.first()) {
if glyph.is(repha) {
pos = 1;
}
}
glyphs.insert(pos, dotted_circle);
Ok(())
}
fn script(indic1_tag: u32) -> Script {
match indic1_tag {
tag::DEVA => Script::Devanagari,
tag::BENG => Script::Bengali,
tag::GURU => Script::Gurmukhi,
tag::GUJR => Script::Gujarati,
tag::ORYA => Script::Oriya,
tag::TAML => Script::Tamil,
tag::TELU => Script::Telugu,
tag::KNDA => Script::Kannada,
tag::MLYM => Script::Malayalam,
tag::SINH => Script::Sinhala,
_ => panic!("Expected an Indic1 script tag"),
}
}
fn indic2_tag(indic1_tag: u32) -> u32 {
match indic1_tag {
tag::DEVA => tag::DEV2,
tag::BENG => tag::BNG2,
tag::GURU => tag::GUR2,
tag::GUJR => tag::GJR2,
tag::ORYA => tag::ORY2,
tag::TAML => tag::TML2,
tag::TELU => tag::TEL2,
tag::KNDA => tag::KND2,
tag::MLYM => tag::MLM2,
tag::SINH => tag::SINH,
_ => panic!("Expected an Indic1 script tag"),
}
}
fn to_indic_syllables(mut glyphs: &[RawGlyph<()>]) -> Vec<(Vec<RawGlyphIndic>, Option<Syllable>)> {
let mut syllables: Vec<(Vec<RawGlyphIndic>, Option<Syllable>)> = Vec::new();
while !glyphs.is_empty() {
let len = match match_syllable(glyphs) {
Some((len, syllable_type)) => {
assert_ne!(len, 0);
let syllable = glyphs[..len].iter().map(to_raw_glyph_indic).collect();
syllables.push((syllable, Some(syllable_type)));
len
}
None => {
let invalid_glyph = to_raw_glyph_indic(&glyphs[0]);
match syllables.last_mut() {
Some((invalid_syllable, None)) => invalid_syllable.push(invalid_glyph),
_ => syllables.push((vec![invalid_glyph], None)),
}
1
}
};
glyphs = &glyphs[len..];
}
syllables
}
fn initial_reorder_consonant_syllable(
shaping_data: &IndicShapingData<'_>,
glyphs: &mut [RawGlyphIndic],
) -> Result<(), ShapingError> {
let base_index = match shaping_data.script.base_consonant_pos() {
BasePos::Last => tag_consonants(shaping_data, glyphs),
BasePos::LastSinhala => Ok(None),
}?;
match base_index {
Some(base_index) => {
initial_reorder_consonant_syllable_with_base(shaping_data, base_index, glyphs)
}
None => initial_reorder_consonant_syllable_without_base(glyphs),
}
}
fn initial_reorder_consonant_syllable_with_base(
shaping_data: &IndicShapingData<'_>,
base_index: usize,
glyphs: &mut [RawGlyphIndic],
) -> Result<(), ShapingError> {
let glyphs_without_pos = glyphs.iter_mut().filter(|g| g.pos().is_none());
for glyph in glyphs_without_pos {
if let GlyphOrigin::Char(c) = glyph.glyph_origin {
let pos = matra_pos(c, shaping_data.script);
glyph.replace_none_pos(pos);
}
}
fn smvd_mark(c: char) -> bool {
match shaping_class(c) {
Some(ShapingClass::Bindu)
| Some(ShapingClass::Visarga)
| Some(ShapingClass::Avagraha)
| Some(ShapingClass::Cantillation)
| Some(ShapingClass::SyllableModifier)
| Some(ShapingClass::GeminationMark)
| Some(ShapingClass::Symbol) => true,
_ => false,
}
}
fn remaining_mark(c: char) -> bool {
match shaping_class(c) {
Some(ShapingClass::Nukta)
| Some(ShapingClass::Virama)
| Some(ShapingClass::PureKiller)
| Some(ShapingClass::Joiner)
| Some(ShapingClass::NonJoiner) => true,
_ => false,
}
}
let glyphs_smvd = glyphs.iter_mut().filter(|g| g.is(smvd_mark));
for glyph in glyphs_smvd {
let pos = match glyph.glyph_origin {
GlyphOrigin::Char('\u{0B01}') => Pos::BeforeSubjoined,
_ => Pos::SMVD,
};
glyph.replace_none_pos(Some(pos));
}
let mut prev_pos = None;
for i in 0..glyphs.len() {
if glyphs[i].is(remaining_mark) && prev_pos.is_some() {
if glyphs[i].is(halant) && prev_pos == Some(Pos::PrebaseMatra) {
let first_non_matra_pos = glyphs[..i]
.iter()
.rev()
.filter_map(RawGlyphIndic::pos)
.find(|pos| *pos != Pos::PrebaseMatra);
if first_non_matra_pos.is_some() {
glyphs[i].replace_none_pos(first_non_matra_pos);
}
} else {
glyphs[i].replace_none_pos(prev_pos);
}
} else if !glyphs[i].is(smvd_mark) {
assert_ne!(glyphs[i].pos(), None);
prev_pos = glyphs[i].pos();
}
}
let mut next_pos = None;
for glyph in glyphs[(base_index + 1)..].iter_mut().rev() {
if glyph.is(remaining_mark) && next_pos.is_some() {
glyph.set_pos(next_pos);
} else if glyph.is(effectively_consonant) {
assert_ne!(glyph.pos(), None);
next_pos = glyph.pos();
}
}
if glyphs.iter().any(|g| g.pos().is_none()) {
return Err(IndicError::MissingTags.into());
} else {
glyphs.sort_by_key(|g| g.pos());
}
let base_index = glyphs
.iter()
.position(|g| g.has_pos(Pos::SyllableBase))
.ok_or_else::<ShapingError, _>(|| IndicError::MissingBaseConsonant.into())?;
if shaping_data.shaping_model == ShapingModel::Indic1 {
let glyphs_post_base = &mut glyphs[(base_index + 1)..];
let first_halant = glyphs_post_base.iter().position(|g| g.is(halant));
let last_consonant = glyphs_post_base
.iter()
.rposition(|g| g.is(effectively_consonant));
if let (Some(first_halant), Some(last_consonant)) = (first_halant, last_consonant) {
if shaping_data.script == Script::Kannada {
let has_halant_after_last_consonant = glyphs_post_base[(last_consonant + 1)..]
.iter()
.rev()
.any(|g| g.is(halant));
if !has_halant_after_last_consonant {
move_element(glyphs_post_base, first_halant, last_consonant);
}
} else {
move_element(glyphs_post_base, first_halant, last_consonant);
}
}
}
for glyph in glyphs.iter_mut() {
let mask = match glyph.pos() {
Some(Pos::RaToBecomeReph) => BasicFeature::Rphf.mask(),
Some(Pos::PrebaseConsonant) => {
if shaping_data.shaping_model != ShapingModel::Indic1
&& shaping_data.script.blwf_mode() == BlwfMode::PreAndPost
{
BasicFeature::Half.mask() | BasicFeature::Blwf.mask()
} else {
BasicFeature::Half.mask()
}
}
Some(Pos::BelowbaseConsonant) => BasicFeature::Blwf.mask(),
Some(Pos::PostbaseConsonant) => BasicFeature::Pstf.mask(),
_ => GsubFeatureMask::empty(),
};
glyph.add_mask(mask);
}
let last_explicit_half_form_index = glyphs[..base_index]
.windows(2)
.rposition(|gs| gs[0].is(halant) && gs[1].is(zwj))
.map(|i| i + 1);
if let Some(last_explicit_half_form_index) = last_explicit_half_form_index {
glyphs[..=last_explicit_half_form_index]
.iter_mut()
.for_each(|g| g.remove_mask(BasicFeature::Blwf.mask()));
}
if shaping_data.shaping_model == ShapingModel::Indic1
&& shaping_data.script == Script::Devanagari
{
let mut ra_indices = Vec::new();
let mut iter = glyphs[..(base_index + 1)].windows(3).enumerate();
while let Some((i, [g0, g1, g2])) = iter.next() {
if g0.is(ra) && g1.is(halant) && !g2.is(zwj) {
ra_indices.push(i)
}
}
let mask = BasicFeature::Blwf.mask();
for i in ra_indices {
glyphs[i].add_mask(mask);
glyphs[i + 1].add_mask(mask);
}
}
if shaping_data.script == Script::Malayalam || shaping_data.script == Script::Telugu {
let glyphs_post_base = &mut glyphs[(base_index + 1)..];
let prebase_reordering_ra_index = match shaping_data.shaping_model {
ShapingModel::Indic1 => glyphs_post_base
.windows(2)
.position(|gs| gs[0].is(ra) && gs[1].is(halant)),
ShapingModel::Indic2 => glyphs_post_base
.windows(2)
.position(|gs| gs[0].is(halant) && gs[1].is(ra)),
};
if let Some(prebase_reordering_ra_index) = prebase_reordering_ra_index {
if shaping_data.feature_would_apply(
BasicFeature::Pref.tag(),
glyphs_post_base,
prebase_reordering_ra_index,
)? {
let mask = BasicFeature::Pref.mask();
glyphs_post_base[prebase_reordering_ra_index].add_mask(mask);
glyphs_post_base[prebase_reordering_ra_index + 1].add_mask(mask);
}
}
}
Ok(())
}
fn initial_reorder_consonant_syllable_without_base(
glyphs: &mut [RawGlyphIndic],
) -> Result<(), ShapingError> {
for glyph in glyphs.iter_mut() {
let mask = match glyph.pos() {
Some(Pos::RaToBecomeReph) => BasicFeature::Rphf.mask(),
_ => BasicFeature::Half.mask(),
};
glyph.add_mask(mask);
}
Ok(())
}
fn tag_consonants(
shaping_data: &IndicShapingData<'_>,
glyphs: &mut [RawGlyphIndic],
) -> Result<Option<usize>, ShapingError> {
let has_reph = would_apply_reph(shaping_data, &glyphs)?;
let (start_prebase_index, mut base_index) = match shaping_data.script.reph_mode() {
RephMode::Implicit if has_reph => (2, Some(0)),
RephMode::Explicit if has_reph => (3, None),
RephMode::LogicalRepha if has_reph => (1, None),
_ => (0, None),
};
if glyphs[start_prebase_index].is(effectively_consonant) {
base_index = Some(start_prebase_index);
}
let mut i = glyphs.len() - 1;
let mut seen_belowbase = false;
while i > start_prebase_index {
if glyphs[i - 1].is(halant) && glyphs[i].is(effectively_consonant) {
if shaping_data.shaping_model == ShapingModel::Indic1 {
glyphs.swap(i, i - 1);
}
let pos_to_apply = postbase_tag(shaping_data, seen_belowbase, glyphs, i - 1)?;
if pos_to_apply == Some(Pos::BelowbaseConsonant) {
seen_belowbase = true;
}
if shaping_data.shaping_model == ShapingModel::Indic1 {
glyphs.swap(i, i - 1);
}
if pos_to_apply.is_some() {
glyphs[i].replace_none_pos(pos_to_apply);
i -= 2;
} else {
base_index = Some(i);
break;
}
} else if !glyphs[i - 1].is(halant) && glyphs[i].is(effectively_consonant) {
base_index = Some(i);
break;
} else if glyphs[i - 1].is(halant) && glyphs[i].is(zwj) {
base_index = None;
break;
} else {
i -= 1;
}
}
tag_consonant_medials(glyphs);
if let Some(base_index) = base_index {
glyphs[base_index].replace_none_pos(Some(Pos::SyllableBase));
glyphs[..base_index]
.iter_mut()
.filter(|g| g.is(effectively_consonant))
.for_each(|g| g.replace_none_pos(Some(Pos::PrebaseConsonant)));
if has_reph && base_index > 0 {
glyphs[0].set_pos(Some(Pos::RaToBecomeReph));
}
Ok(Some(base_index))
} else {
if has_reph {
glyphs[0].replace_none_pos(Some(Pos::RaToBecomeReph));
}
Ok(None)
}
}
fn postbase_tag(
shaping_data: &IndicShapingData<'_>,
seen_belowbase: bool,
glyphs: &mut [RawGlyphIndic],
start_index: usize,
) -> Result<Option<Pos>, ShapingError> {
const FEATURE_POS_PAIRS: &[(BasicFeature, Pos)] = &[
(BasicFeature::Blwf, Pos::BelowbaseConsonant),
(BasicFeature::Pstf, Pos::PostbaseConsonant),
(BasicFeature::Pref, Pos::PostbaseConsonant),
];
let applicable_feature_pos_pairs = if seen_belowbase {
&FEATURE_POS_PAIRS[..1]
} else {
match shaping_data.script {
Script::Malayalam | Script::Telugu => &FEATURE_POS_PAIRS,
_ => &FEATURE_POS_PAIRS[..2],
}
};
for (basic_feature, pos) in applicable_feature_pos_pairs {
if shaping_data.feature_would_apply(basic_feature.tag(), glyphs, start_index)? {
return Ok(Some(*pos));
}
}
Ok(None)
}
fn tag_consonant_medials(glyphs: &mut [RawGlyphIndic]) {
glyphs
.iter_mut()
.filter(|g| g.is(consonant_medial))
.for_each(|g| g.replace_none_pos(Some(Pos::BelowbaseConsonant)))
}
fn would_apply_reph(
shaping_data: &IndicShapingData<'_>,
glyphs: &[RawGlyphIndic],
) -> Result<bool, ShapingError> {
match shaping_data.script.reph_mode() {
RephMode::Implicit => {
match glyphs.get(..3) {
Some([g0, g1, g2]) if g0.is(ra) && g1.is(halant) && !g2.is(zwj) => shaping_data
.feature_would_apply(BasicFeature::Rphf.tag(), glyphs, 0)
.map_err(|e| e.into()),
Some(_) | None => Ok(false),
}
}
RephMode::Explicit => {
match glyphs.get(..3) {
Some([g0, g1, g2]) if g0.is(ra) && g1.is(halant) && g2.is(zwj) => shaping_data
.feature_would_apply(BasicFeature::Rphf.tag(), glyphs, 0)
.map_err(|e| e.into()),
Some(_) | None => Ok(false),
}
}
RephMode::LogicalRepha => glyphs
.first()
.map(|g| g.is(repha))
.ok_or(IndicError::EmptyBuffer.into()),
}
}
fn matra_pos(c: char, script: Script) -> Option<Pos> {
match c {
'\u{0AC9}' => return Some(Pos::AfterPost),
'\u{0B57}' => return Some(Pos::AfterPost),
_ => {}
}
match indic_character(c) {
(Some(ShapingClass::VowelDependent), Some(mark_placement)) => match mark_placement {
MarkPlacementSubclass::TopPosition => script.abovebase_matra_pos(),
MarkPlacementSubclass::RightPosition => script.rightside_matra_pos(c),
MarkPlacementSubclass::BottomPosition => Some(script.belowbase_matra_pos()),
MarkPlacementSubclass::LeftPosition => Some(Pos::PrebaseMatra),
_ => None,
},
_ => None,
}
}
fn apply_basic_features(
shaping_data: &IndicShapingData<'_>,
glyphs: &mut Vec<RawGlyphIndic>,
) -> Result<(), ParseError> {
for feature in BasicFeature::ALL {
let index = shaping_data.get_lookups_cache_index(feature.mask())?;
let lookups = &shaping_data.gsub_cache.cached_lookups.borrow()[index];
for &(lookup_index, feature_tag) in lookups {
shaping_data.apply_lookup(lookup_index, feature_tag, glyphs, |g| {
feature.is_global() || g.has_mask(feature.mask())
})?;
}
}
Ok(())
}
fn final_reorder_consonant_syllable(
shaping_data: &IndicShapingData<'_>,
glyphs: &mut [RawGlyphIndic],
) {
let mut opt_base_index = glyphs.iter().position(|g| g.has_pos(Pos::SyllableBase));
if let (Script::Malayalam, Some(base_index)) = (shaping_data.script, opt_base_index) {
let start = base_index + 1;
opt_base_index = glyphs[start..]
.iter()
.rposition(|g| g.is(effectively_consonant) && g.has_pos(Pos::BelowbaseConsonant))
.map(|i| i + start)
.or(opt_base_index);
}
if let Some(base_index) = opt_base_index {
let first_prebase_matra_index = glyphs[..base_index]
.iter()
.position(|g| g.has_pos(Pos::PrebaseMatra));
let last_prebase_matra_index = glyphs[..base_index]
.iter()
.rposition(|g| g.has_pos(Pos::PrebaseMatra));
if let (Some(first_prebase_matra_index), Some(last_prebase_matra_index)) =
(first_prebase_matra_index, last_prebase_matra_index)
{
if let Some(final_prebase_matra_index) = final_pre_base_matra_index(
shaping_data.script,
last_prebase_matra_index,
base_index,
glyphs,
) {
glyphs[first_prebase_matra_index..=final_prebase_matra_index]
.rotate_left(last_prebase_matra_index - first_prebase_matra_index + 1);
}
}
}
if let Some(final_reph_index) = final_reph_index(shaping_data.script, opt_base_index, glyphs) {
move_element(glyphs, 0, final_reph_index);
opt_base_index = opt_base_index.map(|b| if b <= final_reph_index { b - 1 } else { b });
}
if let (Script::Malayalam, Some(base_index)) | (Script::Telugu, Some(base_index)) =
(shaping_data.script, opt_base_index)
{
let mut pref_glyphs = glyphs
.iter()
.enumerate()
.filter(|(_, g)| g.has_mask(BasicFeature::Pref.mask()));
let pref_glyphs_count = pref_glyphs.clone().count();
if let (Some((reordering_ra_index, _)), 1) = (pref_glyphs.next(), pref_glyphs_count) {
let final_reordering_ra_index =
final_pre_base_reordering_consonant_index(shaping_data.script, base_index, glyphs);
move_element(glyphs, reordering_ra_index, final_reordering_ra_index);
}
}
}
fn final_pre_base_matra_index(
script: Script,
last_prebase_matra_index: usize,
base_index: usize,
glyphs: &[RawGlyphIndic],
) -> Option<usize> {
if script == Script::Malayalam || script == Script::Tamil {
return Some(base_index - 1);
}
let start = last_prebase_matra_index + 1;
glyphs[start..=base_index]
.windows(2)
.rposition(|gs| gs[0].is(halant) && !gs[1].is(zwj))
.map(|i| i + start)
}
fn final_pre_base_reordering_consonant_index(
script: Script,
base_index: usize,
glyphs: &[RawGlyphIndic],
) -> usize {
if script == Script::Malayalam {
return base_index;
}
let mut iter = glyphs[..=base_index].windows(2).enumerate().rev();
while let Some((i, [g0, g1])) = iter.next() {
if g0.is(halant) {
if g1.is(zwj) {
return i + 2;
}
return i + 1;
}
}
base_index
}
fn final_reph_index(
script: Script,
base_index: Option<usize>,
glyphs: &[RawGlyphIndic],
) -> Option<usize> {
if glyphs.first().and_then(RawGlyphIndic::pos) != Some(Pos::RaToBecomeReph) {
return None;
}
let reph_characteristic = script.reph_position();
if let Some(base_index) = base_index {
let start = 1;
let mut iter = glyphs[start..=base_index].windows(2).enumerate();
while let Some((i, [g0, g1])) = iter.next() {
if g0.is(halant) {
if g1.is(zwj) || g1.is(zwnj) {
return Some(i + 1 + start);
}
return Some(i + start);
}
}
}
let reordering_class = match reph_characteristic {
Pos::BeforePost => Some(Pos::AfterPost),
_ => Some(reph_characteristic),
};
let new_index = glyphs
.iter()
.rposition(|g| g.pos() <= reordering_class)
.unwrap_or_else(|| glyphs.len() - 1);
match (glyphs.get(new_index - 1), glyphs.get(new_index)) {
(Some(g0), Some(g1)) if g0.is(matra) && g1.is(halant) => Some(new_index - 1),
_ => Some(new_index),
}
}
fn apply_presentation_features(
shaping_data: &IndicShapingData<'_>,
is_first_syllable: bool,
glyphs: &mut Vec<RawGlyphIndic>,
) -> Result<(), ParseError> {
let mut features = GsubFeatureMask::PRES
| GsubFeatureMask::ABVS
| GsubFeatureMask::BLWS
| GsubFeatureMask::PSTS
| GsubFeatureMask::HALN
| GsubFeatureMask::CALT;
if let Some(glyph) = glyphs.first_mut() {
if is_first_syllable && glyph.has_pos(Pos::PrebaseMatra) {
glyph.add_mask(GsubFeatureMask::INIT);
features |= GsubFeatureMask::INIT;
}
}
let index = shaping_data.get_lookups_cache_index(features)?;
let lookups = &shaping_data.gsub_cache.cached_lookups.borrow()[index];
for &(lookup_index, feature_tag) in lookups {
shaping_data.apply_lookup(lookup_index, feature_tag, glyphs, |g| {
feature_tag != tag::INIT || g.has_mask(GsubFeatureMask::INIT)
})?;
}
Ok(())
}
pub fn gpos_apply_indic(
gpos_cache: &LayoutCache<GPOS>,
gpos_table: &LayoutTable<GPOS>,
gdef_table: Option<&GDEFTable>,
indic1_tag: u32,
opt_lang_tag: Option<u32>,
infos: &mut [Info],
) -> Result<(), ParseError> {
let indic2_tag = indic2_tag(indic1_tag);
let script_table = match gpos_table.find_script(indic2_tag)? {
Some(script_table) => script_table,
None => match gpos_table.find_script_or_default(indic1_tag)? {
Some(script_table) => script_table,
None => return Ok(()),
},
};
let langsys = match script_table.find_langsys_or_default(opt_lang_tag)? {
Some(langsys) => langsys,
None => return Ok(()),
};
const FEATURES: &[u32] = &[
tag::KERN,
tag::MARK,
tag::MKMK,
tag::DIST,
tag::ABVM,
tag::BLWM,
];
gpos::apply_features(
&gpos_cache,
gpos_table,
gdef_table,
&langsys,
FEATURES,
infos,
)
}
fn to_raw_glyph_indic(glyph: &RawGlyph<()>) -> RawGlyphIndic {
RawGlyphIndic {
unicodes: glyph.unicodes.clone(),
glyph_index: glyph.glyph_index,
liga_component_pos: glyph.liga_component_pos,
glyph_origin: glyph.glyph_origin,
small_caps: glyph.small_caps,
multi_subst_dup: glyph.multi_subst_dup,
is_vert_alt: glyph.is_vert_alt,
fake_bold: glyph.fake_bold,
fake_italic: glyph.fake_italic,
variation: glyph.variation,
extra_data: IndicData {
pos: None,
mask: GsubFeatureMask::empty(),
},
}
}
fn from_raw_glyph_indic(glyph: RawGlyphIndic) -> RawGlyph<()> {
RawGlyph {
unicodes: glyph.unicodes,
glyph_index: glyph.glyph_index,
liga_component_pos: glyph.liga_component_pos,
glyph_origin: glyph.glyph_origin,
small_caps: glyph.small_caps,
multi_subst_dup: glyph.multi_subst_dup,
is_vert_alt: glyph.is_vert_alt,
fake_bold: glyph.fake_bold,
fake_italic: glyph.fake_italic,
variation: glyph.variation,
extra_data: (),
}
}
fn effectively_consonant(c: char) -> bool {
match shaping_class(c) {
Some(ShapingClass::Consonant)
| Some(ShapingClass::ConsonantDead)
| Some(ShapingClass::ConsonantPlaceholder)
| Some(ShapingClass::ConsonantWithStacker)
| Some(ShapingClass::DottedCircle)
| Some(ShapingClass::Placeholder)
| Some(ShapingClass::VowelIndependent) => true,
_ => false,
}
}
fn move_element<T>(slice: &mut [T], from: usize, to: usize) {
if from < to {
slice[from..=to].rotate_left(1);
} else {
slice[to..=from].rotate_right(1);
}
}
#[rustfmt::skip]
fn indic_character(ch: char) -> (Option<ShapingClass>, Option<MarkPlacementSubclass>) {
use MarkPlacementSubclass::*;
use ShapingClass::*;
match ch as u32 {
0x0900 => (Some(Bindu), Some(TopPosition)),
0x0901 => (Some(Bindu), Some(TopPosition)),
0x0902 => (Some(Bindu), Some(TopPosition)),
0x0903 => (Some(Visarga), Some(RightPosition)),
0x0904 => (Some(VowelIndependent), None),
0x0905 => (Some(VowelIndependent), None),
0x0906 => (Some(VowelIndependent), None),
0x0907 => (Some(VowelIndependent), None),
0x0908 => (Some(VowelIndependent), None),
0x0909 => (Some(VowelIndependent), None),
0x090A => (Some(VowelIndependent), None),
0x090B => (Some(VowelIndependent), None),
0x090C => (Some(VowelIndependent), None),
0x090D => (Some(VowelIndependent), None),
0x090E => (Some(VowelIndependent), None),
0x090F => (Some(VowelIndependent), None),
0x0910 => (Some(VowelIndependent), None),
0x0911 => (Some(VowelIndependent), None),
0x0912 => (Some(VowelIndependent), None),
0x0913 => (Some(VowelIndependent), None),
0x0914 => (Some(VowelIndependent), None),
0x0915 => (Some(Consonant), None),
0x0916 => (Some(Consonant), None),
0x0917 => (Some(Consonant), None),
0x0918 => (Some(Consonant), None),
0x0919 => (Some(Consonant), None),
0x091A => (Some(Consonant), None),
0x091B => (Some(Consonant), None),
0x091C => (Some(Consonant), None),
0x091D => (Some(Consonant), None),
0x091E => (Some(Consonant), None),
0x091F => (Some(Consonant), None),
0x0920 => (Some(Consonant), None),
0x0921 => (Some(Consonant), None),
0x0922 => (Some(Consonant), None),
0x0923 => (Some(Consonant), None),
0x0924 => (Some(Consonant), None),
0x0925 => (Some(Consonant), None),
0x0926 => (Some(Consonant), None),
0x0927 => (Some(Consonant), None),
0x0928 => (Some(Consonant), None),
0x0929 => (Some(Consonant), None),
0x092A => (Some(Consonant), None),
0x092B => (Some(Consonant), None),
0x092C => (Some(Consonant), None),
0x092D => (Some(Consonant), None),
0x092E => (Some(Consonant), None),
0x092F => (Some(Consonant), None),
0x0930 => (Some(Consonant), None),
0x0931 => (Some(Consonant), None),
0x0932 => (Some(Consonant), None),
0x0933 => (Some(Consonant), None),
0x0934 => (Some(Consonant), None),
0x0935 => (Some(Consonant), None),
0x0936 => (Some(Consonant), None),
0x0937 => (Some(Consonant), None),
0x0938 => (Some(Consonant), None),
0x0939 => (Some(Consonant), None),
0x093A => (Some(VowelDependent), Some(TopPosition)),
0x093B => (Some(VowelDependent), Some(RightPosition)),
0x093C => (Some(Nukta), Some(BottomPosition)),
0x093D => (Some(Avagraha), None),
0x093E => (Some(VowelDependent), Some(RightPosition)),
0x093F => (Some(VowelDependent), Some(LeftPosition)),
0x0940 => (Some(VowelDependent), Some(RightPosition)),
0x0941 => (Some(VowelDependent), Some(BottomPosition)),
0x0942 => (Some(VowelDependent), Some(BottomPosition)),
0x0943 => (Some(VowelDependent), Some(BottomPosition)),
0x0944 => (Some(VowelDependent), Some(BottomPosition)),
0x0945 => (Some(VowelDependent), Some(TopPosition)),
0x0946 => (Some(VowelDependent), Some(TopPosition)),
0x0947 => (Some(VowelDependent), Some(TopPosition)),
0x0948 => (Some(VowelDependent), Some(TopPosition)),
0x0949 => (Some(VowelDependent), Some(RightPosition)),
0x094A => (Some(VowelDependent), Some(RightPosition)),
0x094B => (Some(VowelDependent), Some(RightPosition)),
0x094C => (Some(VowelDependent), Some(RightPosition)),
0x094D => (Some(Virama), Some(BottomPosition)),
0x094E => (Some(VowelDependent), Some(LeftPosition)),
0x094F => (Some(VowelDependent), Some(RightPosition)),
0x0950 => (None, None),
0x0951 => (Some(Cantillation), Some(TopPosition)),
0x0952 => (Some(Cantillation), Some(BottomPosition)),
0x0953 => (None, Some(TopPosition)),
0x0954 => (None, Some(TopPosition)),
0x0955 => (Some(VowelDependent), Some(TopPosition)),
0x0956 => (Some(VowelDependent), Some(BottomPosition)),
0x0957 => (Some(VowelDependent), Some(BottomPosition)),
0x0958 => (Some(Consonant), None),
0x0959 => (Some(Consonant), None),
0x095A => (Some(Consonant), None),
0x095B => (Some(Consonant), None),
0x095C => (Some(Consonant), None),
0x095D => (Some(Consonant), None),
0x095E => (Some(Consonant), None),
0x095F => (Some(Consonant), None),
0x0960 => (Some(VowelIndependent), None),
0x0961 => (Some(VowelIndependent), None),
0x0962 => (Some(VowelDependent), Some(BottomPosition)),
0x0963 => (Some(VowelDependent), Some(BottomPosition)),
0x0964 => (None, None),
0x0965 => (None, None),
0x0966 => (Some(Number), None),
0x0967 => (Some(Number), None),
0x0968 => (Some(Number), None),
0x0969 => (Some(Number), None),
0x096A => (Some(Number), None),
0x096B => (Some(Number), None),
0x096C => (Some(Number), None),
0x096D => (Some(Number), None),
0x096E => (Some(Number), None),
0x096F => (Some(Number), None),
0x0970 => (None, None),
0x0971 => (None, None),
0x0972 => (Some(VowelIndependent), None),
0x0973 => (Some(VowelIndependent), None),
0x0974 => (Some(VowelIndependent), None),
0x0975 => (Some(VowelIndependent), None),
0x0976 => (Some(VowelIndependent), None),
0x0977 => (Some(VowelIndependent), None),
0x0978 => (Some(Consonant), None),
0x0979 => (Some(Consonant), None),
0x097A => (Some(Consonant), None),
0x097B => (Some(Consonant), None),
0x097C => (Some(Consonant), None),
0x097D => (Some(Consonant), None),
0x097E => (Some(Consonant), None),
0x097F => (Some(Consonant), None),
0x0980 => (Some(ConsonantPlaceholder), None),
0x0981 => (Some(Bindu), Some(TopPosition)),
0x0982 => (Some(Bindu), Some(RightPosition)),
0x0983 => (Some(Visarga), Some(RightPosition)),
0x0984 => (None, None),
0x0985 => (Some(VowelIndependent), None),
0x0986 => (Some(VowelIndependent), None),
0x0987 => (Some(VowelIndependent), None),
0x0988 => (Some(VowelIndependent), None),
0x0989 => (Some(VowelIndependent), None),
0x098A => (Some(VowelIndependent), None),
0x098B => (Some(VowelIndependent), None),
0x098C => (Some(VowelIndependent), None),
0x098D => (None, None),
0x098E => (None, None),
0x098F => (Some(VowelIndependent), None),
0x0990 => (Some(VowelIndependent), None),
0x0991 => (None, None),
0x0992 => (None, None),
0x0993 => (Some(VowelIndependent), None),
0x0994 => (Some(VowelIndependent), None),
0x0995 => (Some(Consonant), None),
0x0996 => (Some(Consonant), None),
0x0997 => (Some(Consonant), None),
0x0998 => (Some(Consonant), None),
0x0999 => (Some(Consonant), None),
0x099A => (Some(Consonant), None),
0x099B => (Some(Consonant), None),
0x099C => (Some(Consonant), None),
0x099D => (Some(Consonant), None),
0x099E => (Some(Consonant), None),
0x099F => (Some(Consonant), None),
0x09A0 => (Some(Consonant), None),
0x09A1 => (Some(Consonant), None),
0x09A2 => (Some(Consonant), None),
0x09A3 => (Some(Consonant), None),
0x09A4 => (Some(Consonant), None),
0x09A5 => (Some(Consonant), None),
0x09A6 => (Some(Consonant), None),
0x09A7 => (Some(Consonant), None),
0x09A8 => (Some(Consonant), None),
0x09A9 => (None, None),
0x09AA => (Some(Consonant), None),
0x09AB => (Some(Consonant), None),
0x09AC => (Some(Consonant), None),
0x09AD => (Some(Consonant), None),
0x09AE => (Some(Consonant), None),
0x09AF => (Some(Consonant), None),
0x09B0 => (Some(Consonant), None),
0x09B1 => (None, None),
0x09B2 => (Some(Consonant), None),
0x09B3 => (None, None),
0x09B4 => (None, None),
0x09B5 => (None, None),
0x09B6 => (Some(Consonant), None),
0x09B7 => (Some(Consonant), None),
0x09B8 => (Some(Consonant), None),
0x09B9 => (Some(Consonant), None),
0x09BA => (None, None),
0x09BB => (None, None),
0x09BC => (Some(Nukta), Some(BottomPosition)),
0x09BD => (Some(Avagraha), None),
0x09BE => (Some(VowelDependent), Some(RightPosition)),
0x09BF => (Some(VowelDependent), Some(LeftPosition)),
0x09C0 => (Some(VowelDependent), Some(RightPosition)),
0x09C1 => (Some(VowelDependent), Some(BottomPosition)),
0x09C2 => (Some(VowelDependent), Some(BottomPosition)),
0x09C3 => (Some(VowelDependent), Some(BottomPosition)),
0x09C4 => (Some(VowelDependent), Some(BottomPosition)),
0x09C5 => (None, None),
0x09C6 => (None, None),
0x09C7 => (Some(VowelDependent), Some(LeftPosition)),
0x09C8 => (Some(VowelDependent), Some(LeftPosition)),
0x09C9 => (None, None),
0x09CA => (None, None),
0x09CB => (Some(VowelDependent), Some(LeftAndRightPosition)),
0x09CC => (Some(VowelDependent), Some(LeftAndRightPosition)),
0x09CD => (Some(Virama), Some(BottomPosition)),
0x09CE => (Some(ConsonantDead), None),
0x09CF => (None, None),
0x09D0 => (None, None),
0x09D1 => (None, None),
0x09D2 => (None, None),
0x09D3 => (None, None),
0x09D4 => (None, None),
0x09D5 => (None, None),
0x09D6 => (None, None),
0x09D7 => (Some(VowelDependent), Some(RightPosition)),
0x09D8 => (None, None),
0x09D9 => (None, None),
0x09DA => (None, None),
0x09DB => (None, None),
0x09DC => (Some(Consonant), None),
0x09DD => (Some(Consonant), None),
0x09DE => (None, None),
0x09DF => (Some(Consonant), None),
0x09E0 => (Some(VowelIndependent), None),
0x09E1 => (Some(VowelIndependent), None),
0x09E2 => (Some(VowelDependent), Some(BottomPosition)),
0x09E3 => (Some(VowelDependent), Some(BottomPosition)),
0x09E4 => (None, None),
0x09E5 => (None, None),
0x09E6 => (Some(Number), None),
0x09E7 => (Some(Number), None),
0x09E8 => (Some(Number), None),
0x09E9 => (Some(Number), None),
0x09EA => (Some(Number), None),
0x09EB => (Some(Number), None),
0x09EC => (Some(Number), None),
0x09ED => (Some(Number), None),
0x09EE => (Some(Number), None),
0x09EF => (Some(Number), None),
0x09F0 => (Some(Consonant), None),
0x09F1 => (Some(Consonant), None),
0x09F2 => (Some(Symbol), None),
0x09F3 => (Some(Symbol), None),
0x09F4 => (Some(Number), None),
0x09F5 => (Some(Number), None),
0x09F6 => (Some(Number), None),
0x09F7 => (Some(Number), None),
0x09F8 => (Some(Number), None),
0x09F9 => (Some(Number), None),
0x09FA => (Some(Symbol), None),
0x09FB => (Some(Symbol), None),
0x09FC => (None, None),
0x09FD => (None, None),
0x09FE => (Some(SyllableModifier), Some(TopPosition)),
0x0A00 => (None, None),
0x0A01 => (Some(Bindu), Some(TopPosition)),
0x0A02 => (Some(Bindu), Some(TopPosition)),
0x0A03 => (Some(Visarga), Some(RightPosition)),
0x0A04 => (None, None),
0x0A05 => (Some(VowelIndependent), None),
0x0A06 => (Some(VowelIndependent), None),
0x0A07 => (Some(VowelIndependent), None),
0x0A08 => (Some(VowelIndependent), None),
0x0A09 => (Some(VowelIndependent), None),
0x0A0A => (Some(VowelIndependent), None),
0x0A0B => (None, None),
0x0A0C => (None, None),
0x0A0D => (None, None),
0x0A0E => (None, None),
0x0A0F => (Some(VowelIndependent), None),
0x0A10 => (Some(VowelIndependent), None),
0x0A11 => (None, None),
0x0A12 => (None, None),
0x0A13 => (Some(VowelIndependent), None),
0x0A14 => (Some(VowelIndependent), None),
0x0A15 => (Some(Consonant), None),
0x0A16 => (Some(Consonant), None),
0x0A17 => (Some(Consonant), None),
0x0A18 => (Some(Consonant), None),
0x0A19 => (Some(Consonant), None),
0x0A1A => (Some(Consonant), None),
0x0A1B => (Some(Consonant), None),
0x0A1C => (Some(Consonant), None),
0x0A1D => (Some(Consonant), None),
0x0A1E => (Some(Consonant), None),
0x0A1F => (Some(Consonant), None),
0x0A20 => (Some(Consonant), None),
0x0A21 => (Some(Consonant), None),
0x0A22 => (Some(Consonant), None),
0x0A23 => (Some(Consonant), None),
0x0A24 => (Some(Consonant), None),
0x0A25 => (Some(Consonant), None),
0x0A26 => (Some(Consonant), None),
0x0A27 => (Some(Consonant), None),
0x0A28 => (Some(Consonant), None),
0x0A29 => (None, None),
0x0A2A => (Some(Consonant), None),
0x0A2B => (Some(Consonant), None),
0x0A2C => (Some(Consonant), None),
0x0A2D => (Some(Consonant), None),
0x0A2E => (Some(Consonant), None),
0x0A2F => (Some(Consonant), None),
0x0A30 => (Some(Consonant), None),
0x0A31 => (None, None),
0x0A32 => (Some(Consonant), None),
0x0A33 => (Some(Consonant), None),
0x0A34 => (None, None),
0x0A35 => (Some(Consonant), None),
0x0A36 => (Some(Consonant), None),
0x0A37 => (None, None),
0x0A38 => (Some(Consonant), None),
0x0A39 => (Some(Consonant), None),
0x0A3A => (None, None),
0x0A3B => (None, None),
0x0A3C => (Some(Nukta), Some(BottomPosition)),
0x0A3D => (None, None),
0x0A3E => (Some(VowelDependent), Some(RightPosition)),
0x0A3F => (Some(VowelDependent), Some(LeftPosition)),
0x0A40 => (Some(VowelDependent), Some(RightPosition)),
0x0A41 => (Some(VowelDependent), Some(BottomPosition)),
0x0A42 => (Some(VowelDependent), Some(BottomPosition)),
0x0A43 => (None, None),
0x0A44 => (None, None),
0x0A45 => (None, None),
0x0A46 => (None, None),
0x0A47 => (Some(VowelDependent), Some(TopPosition)),
0x0A48 => (Some(VowelDependent), Some(TopPosition)),
0x0A49 => (None, None),
0x0A4A => (None, None),
0x0A4B => (Some(VowelDependent), Some(TopPosition)),
0x0A4C => (Some(VowelDependent), Some(TopPosition)),
0x0A4D => (Some(Virama), Some(BottomPosition)),
0x0A4E => (None, None),
0x0A4F => (None, None),
0x0A50 => (None, None),
0x0A51 => (Some(Cantillation), None),
0x0A52 => (None, None),
0x0A53 => (None, None),
0x0A54 => (None, None),
0x0A55 => (None, None),
0x0A56 => (None, None),
0x0A57 => (None, None),
0x0A58 => (None, None),
0x0A59 => (Some(Consonant), None),
0x0A5A => (Some(Consonant), None),
0x0A5B => (Some(Consonant), None),
0x0A5C => (Some(Consonant), None),
0x0A5D => (None, None),
0x0A5E => (Some(Consonant), None),
0x0A5F => (None, None),
0x0A60 => (None, None),
0x0A61 => (None, None),
0x0A62 => (None, None),
0x0A63 => (None, None),
0x0A64 => (None, None),
0x0A65 => (None, None),
0x0A66 => (Some(Number), None),
0x0A67 => (Some(Number), None),
0x0A68 => (Some(Number), None),
0x0A69 => (Some(Number), None),
0x0A6A => (Some(Number), None),
0x0A6B => (Some(Number), None),
0x0A6C => (Some(Number), None),
0x0A6D => (Some(Number), None),
0x0A6E => (Some(Number), None),
0x0A6F => (Some(Number), None),
0x0A70 => (Some(Bindu), Some(TopPosition)),
0x0A71 => (Some(GeminationMark), Some(TopPosition)),
0x0A72 => (Some(ConsonantPlaceholder), None),
0x0A73 => (Some(ConsonantPlaceholder), None),
0x0A74 => (None, None),
0x0A75 => (Some(ConsonantMedial), Some(BottomPosition)),
0x0A76 => (None, None),
0x0A81 => (Some(Bindu), Some(TopPosition)),
0x0A82 => (Some(Bindu), Some(TopPosition)),
0x0A83 => (Some(Visarga), Some(RightPosition)),
0x0A84 => (None, None),
0x0A85 => (Some(VowelIndependent), None),
0x0A86 => (Some(VowelIndependent), None),
0x0A87 => (Some(VowelIndependent), None),
0x0A88 => (Some(VowelIndependent), None),
0x0A89 => (Some(VowelIndependent), None),
0x0A8A => (Some(VowelIndependent), None),
0x0A8B => (Some(VowelIndependent), None),
0x0A8C => (Some(VowelIndependent), None),
0x0A8D => (Some(VowelIndependent), None),
0x0A8E => (None, None),
0x0A8F => (Some(VowelIndependent), None),
0x0A90 => (Some(VowelIndependent), None),
0x0A91 => (Some(VowelIndependent), None),
0x0A92 => (None, None),
0x0A93 => (Some(VowelIndependent), None),
0x0A94 => (Some(VowelIndependent), None),
0x0A95 => (Some(Consonant), None),
0x0A96 => (Some(Consonant), None),
0x0A97 => (Some(Consonant), None),
0x0A98 => (Some(Consonant), None),
0x0A99 => (Some(Consonant), None),
0x0A9A => (Some(Consonant), None),
0x0A9B => (Some(Consonant), None),
0x0A9C => (Some(Consonant), None),
0x0A9D => (Some(Consonant), None),
0x0A9E => (Some(Consonant), None),
0x0A9F => (Some(Consonant), None),
0x0AA0 => (Some(Consonant), None),
0x0AA1 => (Some(Consonant), None),
0x0AA2 => (Some(Consonant), None),
0x0AA3 => (Some(Consonant), None),
0x0AA4 => (Some(Consonant), None),
0x0AA5 => (Some(Consonant), None),
0x0AA6 => (Some(Consonant), None),
0x0AA7 => (Some(Consonant), None),
0x0AA8 => (Some(Consonant), None),
0x0AA9 => (None, None),
0x0AAA => (Some(Consonant), None),
0x0AAB => (Some(Consonant), None),
0x0AAC => (Some(Consonant), None),
0x0AAD => (Some(Consonant), None),
0x0AAE => (Some(Consonant), None),
0x0AAF => (Some(Consonant), None),
0x0AB0 => (Some(Consonant), None),
0x0AB1 => (None, None),
0x0AB2 => (Some(Consonant), None),
0x0AB3 => (Some(Consonant), None),
0x0AB4 => (None, None),
0x0AB5 => (Some(Consonant), None),
0x0AB6 => (Some(Consonant), None),
0x0AB7 => (Some(Consonant), None),
0x0AB8 => (Some(Consonant), None),
0x0AB9 => (Some(Consonant), None),
0x0ABA => (None, None),
0x0ABB => (None, None),
0x0ABC => (Some(Nukta), Some(BottomPosition)),
0x0ABD => (Some(Avagraha), None),
0x0ABE => (Some(VowelDependent), Some(RightPosition)),
0x0ABF => (Some(VowelDependent), Some(LeftPosition)),
0x0AC0 => (Some(VowelDependent), Some(RightPosition)),
0x0AC1 => (Some(VowelDependent), Some(BottomPosition)),
0x0AC2 => (Some(VowelDependent), Some(BottomPosition)),
0x0AC3 => (Some(VowelDependent), Some(BottomPosition)),
0x0AC4 => (Some(VowelDependent), Some(BottomPosition)),
0x0AC5 => (Some(VowelDependent), Some(TopPosition)),
0x0AC6 => (None, None),
0x0AC7 => (Some(VowelDependent), Some(TopPosition)),
0x0AC8 => (Some(VowelDependent), Some(TopPosition)),
0x0AC9 => (Some(VowelDependent), Some(TopAndRightPosition)),
0x0ACA => (None, None),
0x0ACB => (Some(VowelDependent), Some(RightPosition)),
0x0ACC => (Some(VowelDependent), Some(RightPosition)),
0x0ACD => (Some(Virama), Some(BottomPosition)),
0x0ACE => (None, None),
0x0ACF => (None, None),
0x0AD0 => (None, None),
0x0AD1 => (None, None),
0x0AD2 => (None, None),
0x0AD3 => (None, None),
0x0AD4 => (None, None),
0x0AD5 => (None, None),
0x0AD6 => (None, None),
0x0AD7 => (None, None),
0x0AD8 => (None, None),
0x0AD9 => (None, None),
0x0ADA => (None, None),
0x0ADB => (None, None),
0x0ADC => (None, None),
0x0ADD => (None, None),
0x0ADE => (None, None),
0x0ADF => (None, None),
0x0AE0 => (Some(VowelIndependent), None),
0x0AE1 => (Some(VowelIndependent), None),
0x0AE2 => (Some(VowelDependent), Some(BottomPosition)),
0x0AE3 => (Some(VowelDependent), Some(BottomPosition)),
0x0AE4 => (None, None),
0x0AE5 => (None, None),
0x0AE6 => (Some(Number), None),
0x0AE7 => (Some(Number), None),
0x0AE8 => (Some(Number), None),
0x0AE9 => (Some(Number), None),
0x0AEA => (Some(Number), None),
0x0AEB => (Some(Number), None),
0x0AEC => (Some(Number), None),
0x0AED => (Some(Number), None),
0x0AEE => (Some(Number), None),
0x0AEF => (Some(Number), None),
0x0AF0 => (Some(Symbol), None),
0x0AF1 => (Some(Symbol), None),
0x0AF2 => (None, None),
0x0AF3 => (None, None),
0x0AF4 => (None, None),
0x0AF5 => (None, None),
0x0AF6 => (None, None),
0x0AF7 => (None, None),
0x0AF8 => (None, None),
0x0AF9 => (Some(Consonant), None),
0x0AFA => (Some(Cantillation), Some(TopPosition)),
0x0AFB => (Some(Cantillation), Some(TopPosition)),
0x0AFC => (Some(Cantillation), Some(TopPosition)),
0x0AFD => (Some(Nukta), Some(TopPosition)),
0x0AFE => (Some(Nukta), Some(TopPosition)),
0x0AFF => (Some(Nukta), Some(TopPosition)),
0x0B00 => (None, None),
0x0B01 => (Some(Bindu), Some(TopPosition)),
0x0B02 => (Some(Bindu), Some(RightPosition)),
0x0B03 => (Some(Visarga), Some(RightPosition)),
0x0B04 => (None, None),
0x0B05 => (Some(VowelIndependent), None),
0x0B06 => (Some(VowelIndependent), None),
0x0B07 => (Some(VowelIndependent), None),
0x0B08 => (Some(VowelIndependent), None),
0x0B09 => (Some(VowelIndependent), None),
0x0B0A => (Some(VowelIndependent), None),
0x0B0B => (Some(VowelIndependent), None),
0x0B0C => (Some(VowelIndependent), None),
0x0B0D => (None, None),
0x0B0E => (None, None),
0x0B0F => (Some(VowelIndependent), None),
0x0B10 => (Some(VowelIndependent), None),
0x0B11 => (None, None),
0x0B12 => (None, None),
0x0B13 => (Some(VowelIndependent), None),
0x0B14 => (Some(VowelIndependent), None),
0x0B15 => (Some(Consonant), None),
0x0B16 => (Some(Consonant), None),
0x0B17 => (Some(Consonant), None),
0x0B18 => (Some(Consonant), None),
0x0B19 => (Some(Consonant), None),
0x0B1A => (Some(Consonant), None),
0x0B1B => (Some(Consonant), None),
0x0B1C => (Some(Consonant), None),
0x0B1D => (Some(Consonant), None),
0x0B1E => (Some(Consonant), None),
0x0B1F => (Some(Consonant), None),
0x0B20 => (Some(Consonant), None),
0x0B21 => (Some(Consonant), None),
0x0B22 => (Some(Consonant), None),
0x0B23 => (Some(Consonant), None),
0x0B24 => (Some(Consonant), None),
0x0B25 => (Some(Consonant), None),
0x0B26 => (Some(Consonant), None),
0x0B27 => (Some(Consonant), None),
0x0B28 => (Some(Consonant), None),
0x0B29 => (None, None),
0x0B2A => (Some(Consonant), None),
0x0B2B => (Some(Consonant), None),
0x0B2C => (Some(Consonant), None),
0x0B2D => (Some(Consonant), None),
0x0B2E => (Some(Consonant), None),
0x0B2F => (Some(Consonant), None),
0x0B30 => (Some(Consonant), None),
0x0B31 => (None, None),
0x0B32 => (Some(Consonant), None),
0x0B33 => (Some(Consonant), None),
0x0B34 => (None, None),
0x0B35 => (Some(Consonant), None),
0x0B36 => (Some(Consonant), None),
0x0B37 => (Some(Consonant), None),
0x0B38 => (Some(Consonant), None),
0x0B39 => (Some(Consonant), None),
0x0B3A => (None, None),
0x0B3B => (None, None),
0x0B3C => (Some(Nukta), Some(BottomPosition)),
0x0B3D => (Some(Avagraha), None),
0x0B3E => (Some(VowelDependent), Some(RightPosition)),
0x0B3F => (Some(VowelDependent), Some(TopPosition)),
0x0B40 => (Some(VowelDependent), Some(RightPosition)),
0x0B41 => (Some(VowelDependent), Some(BottomPosition)),
0x0B42 => (Some(VowelDependent), Some(BottomPosition)),
0x0B43 => (Some(VowelDependent), Some(BottomPosition)),
0x0B44 => (Some(VowelDependent), Some(BottomPosition)),
0x0B45 => (None, None),
0x0B46 => (None, None),
0x0B47 => (Some(VowelDependent), Some(LeftPosition)),
0x0B48 => (Some(VowelDependent), Some(TopAndLeftPosition)),
0x0B49 => (None, None),
0x0B4A => (None, None),
0x0B4B => (Some(VowelDependent), Some(LeftAndRightPosition)),
0x0B4C => (Some(VowelDependent), Some(TopLeftAndRightPosition)),
0x0B4D => (Some(Virama), Some(BottomPosition)),
0x0B4E => (None, None),
0x0B4F => (None, None),
0x0B50 => (None, None),
0x0B51 => (None, None),
0x0B52 => (None, None),
0x0B53 => (None, None),
0x0B54 => (None, None),
0x0B55 => (None, None),
0x0B56 => (Some(VowelDependent), Some(TopPosition)),
0x0B57 => (Some(VowelDependent), Some(TopAndRightPosition)),
0x0B58 => (None, None),
0x0B59 => (None, None),
0x0B5A => (None, None),
0x0B5B => (None, None),
0x0B5C => (Some(Consonant), None),
0x0B5D => (Some(Consonant), None),
0x0B5E => (None, None),
0x0B5F => (Some(Consonant), None),
0x0B60 => (Some(VowelIndependent), None),
0x0B61 => (Some(VowelIndependent), None),
0x0B62 => (Some(VowelDependent), Some(BottomPosition)),
0x0B63 => (Some(VowelDependent), Some(BottomPosition)),
0x0B64 => (None, None),
0x0B65 => (None, None),
0x0B66 => (Some(Number), None),
0x0B67 => (Some(Number), None),
0x0B68 => (Some(Number), None),
0x0B69 => (Some(Number), None),
0x0B6A => (Some(Number), None),
0x0B6B => (Some(Number), None),
0x0B6C => (Some(Number), None),
0x0B6D => (Some(Number), None),
0x0B6E => (Some(Number), None),
0x0B6F => (Some(Number), None),
0x0B70 => (Some(Symbol), None),
0x0B71 => (Some(Consonant), None),
0x0B72 => (Some(Number), None),
0x0B73 => (Some(Number), None),
0x0B74 => (Some(Number), None),
0x0B75 => (Some(Number), None),
0x0B76 => (Some(Number), None),
0x0B77 => (Some(Number), None),
0x0B78 => (None, None),
0x0B79 => (None, None),
0x0B7A => (None, None),
0x0B7B => (None, None),
0x0B7C => (None, None),
0x0B7D => (None, None),
0x0B7E => (None, None),
0x0B7F => (None, None),
0x0B80 => (None, None),
0x0B81 => (None, None),
0x0B82 => (Some(Bindu), Some(TopPosition)),
0x0B83 => (Some(ModifyingLetter), None),
0x0B84 => (None, None),
0x0B85 => (Some(VowelIndependent), None),
0x0B86 => (Some(VowelIndependent), None),
0x0B87 => (Some(VowelIndependent), None),
0x0B88 => (Some(VowelIndependent), None),
0x0B89 => (Some(VowelIndependent), None),
0x0B8A => (Some(VowelIndependent), None),
0x0B8B => (None, None),
0x0B8C => (None, None),
0x0B8D => (None, None),
0x0B8E => (Some(VowelIndependent), None),
0x0B8F => (Some(VowelIndependent), None),
0x0B90 => (Some(VowelIndependent), None),
0x0B91 => (None, None),
0x0B92 => (Some(VowelIndependent), None),
0x0B93 => (Some(VowelIndependent), None),
0x0B94 => (Some(VowelIndependent), None),
0x0B95 => (Some(Consonant), None),
0x0B96 => (None, None),
0x0B97 => (None, None),
0x0B98 => (None, None),
0x0B99 => (Some(Consonant), None),
0x0B9A => (Some(Consonant), None),
0x0B9B => (None, None),
0x0B9C => (Some(Consonant), None),
0x0B9D => (None, None),
0x0B9E => (Some(Consonant), None),
0x0B9F => (Some(Consonant), None),
0x0BA0 => (None, None),
0x0BA1 => (None, None),
0x0BA2 => (None, None),
0x0BA3 => (Some(Consonant), None),
0x0BA4 => (Some(Consonant), None),
0x0BA5 => (None, None),
0x0BA6 => (None, None),
0x0BA7 => (None, None),
0x0BA8 => (Some(Consonant), None),
0x0BA9 => (Some(Consonant), None),
0x0BAA => (Some(Consonant), None),
0x0BAB => (None, None),
0x0BAC => (None, None),
0x0BAD => (None, None),
0x0BAE => (Some(Consonant), None),
0x0BAF => (Some(Consonant), None),
0x0BB0 => (Some(Consonant), None),
0x0BB1 => (Some(Consonant), None),
0x0BB2 => (Some(Consonant), None),
0x0BB3 => (Some(Consonant), None),
0x0BB4 => (Some(Consonant), None),
0x0BB5 => (Some(Consonant), None),
0x0BB6 => (Some(Consonant), None),
0x0BB7 => (Some(Consonant), None),
0x0BB8 => (Some(Consonant), None),
0x0BB9 => (Some(Consonant), None),
0x0BBA => (None, None),
0x0BBB => (None, None),
0x0BBC => (None, None),
0x0BBD => (None, None),
0x0BBE => (Some(VowelDependent), Some(RightPosition)),
0x0BBF => (Some(VowelDependent), Some(RightPosition)),
0x0BC0 => (Some(VowelDependent), Some(TopPosition)),
0x0BC1 => (Some(VowelDependent), Some(RightPosition)),
0x0BC2 => (Some(VowelDependent), Some(RightPosition)),
0x0BC3 => (None, None),
0x0BC4 => (None, None),
0x0BC5 => (None, None),
0x0BC6 => (Some(VowelDependent), Some(LeftPosition)),
0x0BC7 => (Some(VowelDependent), Some(LeftPosition)),
0x0BC8 => (Some(VowelDependent), Some(LeftPosition)),
0x0BC9 => (None, None),
0x0BCA => (Some(VowelDependent), Some(LeftAndRightPosition)),
0x0BCB => (Some(VowelDependent), Some(LeftAndRightPosition)),
0x0BCC => (Some(VowelDependent), Some(LeftAndRightPosition)),
0x0BCD => (Some(Virama), Some(TopPosition)),
0x0BCE => (None, None),
0x0BCF => (None, None),
0x0BD0 => (None, None),
0x0BD1 => (None, None),
0x0BD2 => (None, None),
0x0BD3 => (None, None),
0x0BD4 => (None, None),
0x0BD5 => (None, None),
0x0BD6 => (None, None),
0x0BD7 => (Some(VowelDependent), Some(RightPosition)),
0x0BD8 => (None, None),
0x0BD9 => (None, None),
0x0BDA => (None, None),
0x0BDB => (None, None),
0x0BDC => (None, None),
0x0BDD => (None, None),
0x0BDE => (None, None),
0x0BDF => (None, None),
0x0BE0 => (None, None),
0x0BE1 => (None, None),
0x0BE2 => (None, None),
0x0BE3 => (None, None),
0x0BE4 => (None, None),
0x0BE5 => (None, None),
0x0BE6 => (Some(Number), None),
0x0BE7 => (Some(Number), None),
0x0BE8 => (Some(Number), None),
0x0BE9 => (Some(Number), None),
0x0BEA => (Some(Number), None),
0x0BEB => (Some(Number), None),
0x0BEC => (Some(Number), None),
0x0BED => (Some(Number), None),
0x0BEE => (Some(Number), None),
0x0BEF => (Some(Number), None),
0x0BF0 => (Some(Number), None),
0x0BF1 => (Some(Number), None),
0x0BF2 => (Some(Number), None),
0x0BF3 => (Some(Symbol), None),
0x0BF4 => (Some(Symbol), None),
0x0BF5 => (Some(Symbol), None),
0x0BF6 => (Some(Symbol), None),
0x0BF7 => (Some(Symbol), None),
0x0BF8 => (Some(Symbol), None),
0x0BF9 => (Some(Symbol), None),
0x0BFA => (Some(Symbol), None),
0x0C00 => (Some(Bindu), Some(TopPosition)),
0x0C01 => (Some(Bindu), Some(RightPosition)),
0x0C02 => (Some(Bindu), Some(RightPosition)),
0x0C03 => (Some(Visarga), Some(RightPosition)),
0x0C04 => (Some(Bindu), Some(TopPosition)),
0x0C05 => (Some(VowelIndependent), None),
0x0C06 => (Some(VowelIndependent), None),
0x0C07 => (Some(VowelIndependent), None),
0x0C08 => (Some(VowelIndependent), None),
0x0C09 => (Some(VowelIndependent), None),
0x0C0A => (Some(VowelIndependent), None),
0x0C0B => (Some(VowelIndependent), None),
0x0C0C => (Some(VowelIndependent), None),
0x0C0D => (None, None),
0x0C0E => (Some(VowelIndependent), None),
0x0C0F => (Some(VowelIndependent), None),
0x0C10 => (Some(VowelIndependent), None),
0x0C11 => (None, None),
0x0C12 => (Some(VowelIndependent), None),
0x0C13 => (Some(VowelIndependent), None),
0x0C14 => (Some(VowelIndependent), None),
0x0C15 => (Some(Consonant), None),
0x0C16 => (Some(Consonant), None),
0x0C17 => (Some(Consonant), None),
0x0C18 => (Some(Consonant), None),
0x0C19 => (Some(Consonant), None),
0x0C1A => (Some(Consonant), None),
0x0C1B => (Some(Consonant), None),
0x0C1C => (Some(Consonant), None),
0x0C1D => (Some(Consonant), None),
0x0C1E => (Some(Consonant), None),
0x0C1F => (Some(Consonant), None),
0x0C20 => (Some(Consonant), None),
0x0C21 => (Some(Consonant), None),
0x0C22 => (Some(Consonant), None),
0x0C23 => (Some(Consonant), None),
0x0C24 => (Some(Consonant), None),
0x0C25 => (Some(Consonant), None),
0x0C26 => (Some(Consonant), None),
0x0C27 => (Some(Consonant), None),
0x0C28 => (Some(Consonant), None),
0x0C29 => (None, None),
0x0C2A => (Some(Consonant), None),
0x0C2B => (Some(Consonant), None),
0x0C2C => (Some(Consonant), None),
0x0C2D => (Some(Consonant), None),
0x0C2E => (Some(Consonant), None),
0x0C2F => (Some(Consonant), None),
0x0C30 => (Some(Consonant), None),
0x0C31 => (Some(Consonant), None),
0x0C32 => (Some(Consonant), None),
0x0C33 => (Some(Consonant), None),
0x0C34 => (Some(Consonant), None),
0x0C35 => (Some(Consonant), None),
0x0C36 => (Some(Consonant), None),
0x0C37 => (Some(Consonant), None),
0x0C38 => (Some(Consonant), None),
0x0C39 => (Some(Consonant), None),
0x0C3A => (None, None),
0x0C3B => (None, None),
0x0C3C => (None, None),
0x0C3D => (Some(Avagraha), None),
0x0C3E => (Some(VowelDependent), Some(TopPosition)),
0x0C3F => (Some(VowelDependent), Some(TopPosition)),
0x0C40 => (Some(VowelDependent), Some(TopPosition)),
0x0C41 => (Some(VowelDependent), Some(RightPosition)),
0x0C42 => (Some(VowelDependent), Some(RightPosition)),
0x0C43 => (Some(VowelDependent), Some(RightPosition)),
0x0C44 => (Some(VowelDependent), Some(RightPosition)),
0x0C45 => (None, None),
0x0C46 => (Some(VowelDependent), Some(TopPosition)),
0x0C47 => (Some(VowelDependent), Some(TopPosition)),
0x0C48 => (Some(VowelDependent), Some(TopAndBottomPosition)),
0x0C49 => (None, None),
0x0C4A => (Some(VowelDependent), Some(TopPosition)),
0x0C4B => (Some(VowelDependent), Some(TopPosition)),
0x0C4C => (Some(VowelDependent), Some(TopPosition)),
0x0C4D => (Some(Virama), Some(TopPosition)),
0x0C4E => (None, None),
0x0C4F => (None, None),
0x0C50 => (None, None),
0x0C51 => (None, None),
0x0C52 => (None, None),
0x0C53 => (None, None),
0x0C54 => (None, None),
0x0C55 => (Some(VowelDependent), Some(TopPosition)),
0x0C56 => (Some(VowelDependent), Some(BottomPosition)),
0x0C57 => (None, None),
0x0C58 => (Some(Consonant), None),
0x0C59 => (Some(Consonant), None),
0x0C5A => (Some(Consonant), None),
0x0C5B => (None, None),
0x0C5C => (None, None),
0x0C5D => (None, None),
0x0C5E => (None, None),
0x0C5F => (None, None),
0x0C60 => (Some(VowelIndependent), None),
0x0C61 => (Some(VowelIndependent), None),
0x0C62 => (Some(VowelDependent), Some(BottomPosition)),
0x0C63 => (Some(VowelDependent), Some(BottomPosition)),
0x0C64 => (None, None),
0x0C65 => (None, None),
0x0C66 => (Some(Number), None),
0x0C67 => (Some(Number), None),
0x0C68 => (Some(Number), None),
0x0C69 => (Some(Number), None),
0x0C6A => (Some(Number), None),
0x0C6B => (Some(Number), None),
0x0C6C => (Some(Number), None),
0x0C6D => (Some(Number), None),
0x0C6E => (Some(Number), None),
0x0C6F => (Some(Number), None),
0x0C70 => (None, None),
0x0C71 => (None, None),
0x0C72 => (None, None),
0x0C73 => (None, None),
0x0C74 => (None, None),
0x0C75 => (None, None),
0x0C76 => (None, None),
0x0C77 => (None, None),
0x0C78 => (Some(Number), None),
0x0C79 => (Some(Number), None),
0x0C7A => (Some(Number), None),
0x0C7B => (Some(Number), None),
0x0C7C => (Some(Number), None),
0x0C7D => (Some(Number), None),
0x0C7E => (Some(Number), None),
0x0C7F => (Some(Symbol), None),
0x0C80 => (None, None),
0x0C81 => (Some(Bindu), Some(TopPosition)),
0x0C82 => (Some(Bindu), Some(RightPosition)),
0x0C83 => (Some(Visarga), Some(RightPosition)),
0x0C84 => (None, None),
0x0C85 => (Some(VowelIndependent), None),
0x0C86 => (Some(VowelIndependent), None),
0x0C87 => (Some(VowelIndependent), None),
0x0C88 => (Some(VowelIndependent), None),
0x0C89 => (Some(VowelIndependent), None),
0x0C8A => (Some(VowelIndependent), None),
0x0C8B => (Some(VowelIndependent), None),
0x0C8C => (Some(VowelIndependent), None),
0x0C8D => (None, None),
0x0C8E => (Some(VowelIndependent), None),
0x0C8F => (Some(VowelIndependent), None),
0x0C90 => (Some(VowelIndependent), None),
0x0C91 => (None, None),
0x0C92 => (Some(VowelIndependent), None),
0x0C93 => (Some(VowelIndependent), None),
0x0C94 => (Some(VowelIndependent), None),
0x0C95 => (Some(Consonant), None),
0x0C96 => (Some(Consonant), None),
0x0C97 => (Some(Consonant), None),
0x0C98 => (Some(Consonant), None),
0x0C99 => (Some(Consonant), None),
0x0C9A => (Some(Consonant), None),
0x0C9B => (Some(Consonant), None),
0x0C9C => (Some(Consonant), None),
0x0C9D => (Some(Consonant), None),
0x0C9E => (Some(Consonant), None),
0x0C9F => (Some(Consonant), None),
0x0CA0 => (Some(Consonant), None),
0x0CA1 => (Some(Consonant), None),
0x0CA2 => (Some(Consonant), None),
0x0CA3 => (Some(Consonant), None),
0x0CA4 => (Some(Consonant), None),
0x0CA5 => (Some(Consonant), None),
0x0CA6 => (Some(Consonant), None),
0x0CA7 => (Some(Consonant), None),
0x0CA8 => (Some(Consonant), None),
0x0CA9 => (None, None),
0x0CAA => (Some(Consonant), None),
0x0CAB => (Some(Consonant), None),
0x0CAC => (Some(Consonant), None),
0x0CAD => (Some(Consonant), None),
0x0CAE => (Some(Consonant), None),
0x0CAF => (Some(Consonant), None),
0x0CB0 => (Some(Consonant), None),
0x0CB1 => (Some(Consonant), None),
0x0CB2 => (Some(Consonant), None),
0x0CB3 => (Some(Consonant), None),
0x0CB4 => (None, None),
0x0CB5 => (Some(Consonant), None),
0x0CB6 => (Some(Consonant), None),
0x0CB7 => (Some(Consonant), None),
0x0CB8 => (Some(Consonant), None),
0x0CB9 => (Some(Consonant), None),
0x0CBA => (None, None),
0x0CBB => (None, None),
0x0CBC => (Some(Nukta), Some(BottomPosition)),
0x0CBD => (Some(Avagraha), None),
0x0CBE => (Some(VowelDependent), Some(RightPosition)),
0x0CBF => (Some(VowelDependent), Some(TopPosition)),
0x0CC0 => (Some(VowelDependent), Some(TopAndRightPosition)),
0x0CC1 => (Some(VowelDependent), Some(RightPosition)),
0x0CC2 => (Some(VowelDependent), Some(RightPosition)),
0x0CC3 => (Some(VowelDependent), Some(RightPosition)),
0x0CC4 => (Some(VowelDependent), Some(RightPosition)),
0x0CC5 => (None, None),
0x0CC6 => (Some(VowelDependent), Some(TopPosition)),
0x0CC7 => (Some(VowelDependent), Some(TopAndRightPosition)),
0x0CC8 => (Some(VowelDependent), Some(TopAndRightPosition)),
0x0CC9 => (None, None),
0x0CCA => (Some(VowelDependent), Some(TopAndRightPosition)),
0x0CCB => (Some(VowelDependent), Some(TopAndRightPosition)),
0x0CCC => (Some(VowelDependent), Some(TopPosition)),
0x0CCD => (Some(Virama), Some(TopPosition)),
0x0CCE => (None, None),
0x0CCF => (None, None),
0x0CD0 => (None, None),
0x0CD1 => (None, None),
0x0CD2 => (None, None),
0x0CD3 => (None, None),
0x0CD4 => (None, None),
0x0CD5 => (Some(VowelDependent), Some(RightPosition)),
0x0CD6 => (Some(VowelDependent), Some(RightPosition)),
0x0CD7 => (None, None),
0x0CD8 => (None, None),
0x0CD9 => (None, None),
0x0CDA => (None, None),
0x0CDB => (None, None),
0x0CDC => (None, None),
0x0CDD => (None, None),
0x0CDE => (Some(Consonant), None),
0x0CDF => (None, None),
0x0CE0 => (Some(VowelIndependent), None),
0x0CE1 => (Some(VowelIndependent), None),
0x0CE2 => (Some(VowelDependent), Some(BottomPosition)),
0x0CE3 => (Some(VowelDependent), Some(BottomPosition)),
0x0CE4 => (None, None),
0x0CE5 => (None, None),
0x0CE6 => (Some(Number), None),
0x0CE7 => (Some(Number), None),
0x0CE8 => (Some(Number), None),
0x0CE9 => (Some(Number), None),
0x0CEA => (Some(Number), None),
0x0CEB => (Some(Number), None),
0x0CEC => (Some(Number), None),
0x0CED => (Some(Number), None),
0x0CEE => (Some(Number), None),
0x0CEF => (Some(Number), None),
0x0CF0 => (None, None),
0x0CF1 => (Some(ConsonantWithStacker), None),
0x0CF2 => (Some(ConsonantWithStacker), None),
0x0D00 => (Some(Bindu), Some(TopPosition)),
0x0D01 => (Some(Bindu), Some(TopPosition)),
0x0D02 => (Some(Bindu), Some(RightPosition)),
0x0D03 => (Some(Visarga), Some(RightPosition)),
0x0D04 => (None, None),
0x0D05 => (Some(VowelIndependent), None),
0x0D06 => (Some(VowelIndependent), None),
0x0D07 => (Some(VowelIndependent), None),
0x0D08 => (Some(VowelIndependent), None),
0x0D09 => (Some(VowelIndependent), None),
0x0D0A => (Some(VowelIndependent), None),
0x0D0B => (Some(VowelIndependent), None),
0x0D0C => (Some(VowelIndependent), None),
0x0D0D => (None, None),
0x0D0E => (Some(VowelIndependent), None),
0x0D0F => (Some(VowelIndependent), None),
0x0D10 => (Some(VowelIndependent), None),
0x0D11 => (None, None),
0x0D12 => (Some(VowelIndependent), None),
0x0D13 => (Some(VowelIndependent), None),
0x0D14 => (Some(VowelIndependent), None),
0x0D15 => (Some(Consonant), None),
0x0D16 => (Some(Consonant), None),
0x0D17 => (Some(Consonant), None),
0x0D18 => (Some(Consonant), None),
0x0D19 => (Some(Consonant), None),
0x0D1A => (Some(Consonant), None),
0x0D1B => (Some(Consonant), None),
0x0D1C => (Some(Consonant), None),
0x0D1D => (Some(Consonant), None),
0x0D1E => (Some(Consonant), None),
0x0D1F => (Some(Consonant), None),
0x0D20 => (Some(Consonant), None),
0x0D21 => (Some(Consonant), None),
0x0D22 => (Some(Consonant), None),
0x0D23 => (Some(Consonant), None),
0x0D24 => (Some(Consonant), None),
0x0D25 => (Some(Consonant), None),
0x0D26 => (Some(Consonant), None),
0x0D27 => (Some(Consonant), None),
0x0D28 => (Some(Consonant), None),
0x0D29 => (Some(Consonant), None),
0x0D2A => (Some(Consonant), None),
0x0D2B => (Some(Consonant), None),
0x0D2C => (Some(Consonant), None),
0x0D2D => (Some(Consonant), None),
0x0D2E => (Some(Consonant), None),
0x0D2F => (Some(Consonant), None),
0x0D30 => (Some(Consonant), None),
0x0D31 => (Some(Consonant), None),
0x0D32 => (Some(Consonant), None),
0x0D33 => (Some(Consonant), None),
0x0D34 => (Some(Consonant), None),
0x0D35 => (Some(Consonant), None),
0x0D36 => (Some(Consonant), None),
0x0D37 => (Some(Consonant), None),
0x0D38 => (Some(Consonant), None),
0x0D39 => (Some(Consonant), None),
0x0D3A => (Some(Consonant), None),
0x0D3B => (Some(PureKiller), Some(TopPosition)),
0x0D3C => (Some(PureKiller), Some(TopPosition)),
0x0D3D => (Some(Avagraha), None),
0x0D3E => (Some(VowelDependent), Some(RightPosition)),
0x0D3F => (Some(VowelDependent), Some(RightPosition)),
0x0D40 => (Some(VowelDependent), Some(RightPosition)),
0x0D41 => (Some(VowelDependent), Some(RightPosition)),
0x0D42 => (Some(VowelDependent), Some(RightPosition)),
0x0D43 => (Some(VowelDependent), Some(BottomPosition)),
0x0D44 => (Some(VowelDependent), Some(BottomPosition)),
0x0D45 => (None, None),
0x0D46 => (Some(VowelDependent), Some(LeftPosition)),
0x0D47 => (Some(VowelDependent), Some(LeftPosition)),
0x0D48 => (Some(VowelDependent), Some(LeftPosition)),
0x0D49 => (None, None),
0x0D4A => (Some(VowelDependent), Some(LeftAndRightPosition)),
0x0D4B => (Some(VowelDependent), Some(LeftAndRightPosition)),
0x0D4C => (Some(VowelDependent), Some(LeftAndRightPosition)),
0x0D4D => (Some(Virama), Some(TopPosition)),
0x0D4E => (Some(ConsonantPreRepha), None),
0x0D4F => (Some(Symbol), None),
0x0D50 => (None, None),
0x0D51 => (None, None),
0x0D52 => (None, None),
0x0D53 => (None, None),
0x0D54 => (Some(ConsonantDead), None),
0x0D55 => (Some(ConsonantDead), None),
0x0D56 => (Some(ConsonantDead), None),
0x0D57 => (Some(VowelDependent), Some(RightPosition)),
0x0D58 => (Some(Number), None),
0x0D59 => (Some(Number), None),
0x0D5A => (Some(Number), None),
0x0D5B => (Some(Number), None),
0x0D5C => (Some(Number), None),
0x0D5D => (Some(Number), None),
0x0D5E => (Some(Number), None),
0x0D5F => (Some(VowelIndependent), None),
0x0D60 => (Some(VowelIndependent), None),
0x0D61 => (Some(VowelIndependent), None),
0x0D62 => (Some(VowelDependent), Some(BottomPosition)),
0x0D63 => (Some(VowelDependent), Some(BottomPosition)),
0x0D64 => (None, None),
0x0D65 => (None, None),
0x0D66 => (Some(Number), None),
0x0D67 => (Some(Number), None),
0x0D68 => (Some(Number), None),
0x0D69 => (Some(Number), None),
0x0D6A => (Some(Number), None),
0x0D6B => (Some(Number), None),
0x0D6C => (Some(Number), None),
0x0D6D => (Some(Number), None),
0x0D6E => (Some(Number), None),
0x0D6F => (Some(Number), None),
0x0D70 => (Some(Number), None),
0x0D71 => (Some(Number), None),
0x0D72 => (Some(Number), None),
0x0D73 => (Some(Number), None),
0x0D74 => (Some(Number), None),
0x0D75 => (Some(Number), None),
0x0D76 => (Some(Number), None),
0x0D77 => (Some(Number), None),
0x0D78 => (Some(Number), None),
0x0D79 => (Some(Symbol), None),
0x0D7A => (Some(ConsonantDead), None),
0x0D7B => (Some(ConsonantDead), None),
0x0D7C => (Some(ConsonantDead), None),
0x0D7D => (Some(ConsonantDead), None),
0x0D7E => (Some(ConsonantDead), None),
0x0D7F => (Some(ConsonantDead), None),
0x0D80 => (None, None),
0x0D81 => (None, None),
0x0D82 => (Some(Bindu), Some(RightPosition)),
0x0D83 => (Some(Visarga), Some(RightPosition)),
0x0D84 => (None, None),
0x0D85 => (Some(VowelIndependent), None),
0x0D86 => (Some(VowelIndependent), None),
0x0D87 => (Some(VowelIndependent), None),
0x0D88 => (Some(VowelIndependent), None),
0x0D89 => (Some(VowelIndependent), None),
0x0D8A => (Some(VowelIndependent), None),
0x0D8B => (Some(VowelIndependent), None),
0x0D8C => (Some(VowelIndependent), None),
0x0D8D => (Some(VowelIndependent), None),
0x0D8E => (Some(VowelIndependent), None),
0x0D8F => (Some(VowelIndependent), None),
0x0D90 => (Some(VowelIndependent), None),
0x0D91 => (Some(VowelIndependent), None),
0x0D92 => (Some(VowelIndependent), None),
0x0D93 => (Some(VowelIndependent), None),
0x0D94 => (Some(VowelIndependent), None),
0x0D95 => (Some(VowelIndependent), None),
0x0D96 => (Some(VowelIndependent), None),
0x0D97 => (None, None),
0x0D98 => (None, None),
0x0D99 => (None, None),
0x0D9A => (Some(Consonant), None),
0x0D9B => (Some(Consonant), None),
0x0D9C => (Some(Consonant), None),
0x0D9D => (Some(Consonant), None),
0x0D9E => (Some(Consonant), None),
0x0D9F => (Some(Consonant), None),
0x0DA0 => (Some(Consonant), None),
0x0DA1 => (Some(Consonant), None),
0x0DA2 => (Some(Consonant), None),
0x0DA3 => (Some(Consonant), None),
0x0DA4 => (Some(Consonant), None),
0x0DA5 => (Some(Consonant), None),
0x0DA6 => (Some(Consonant), None),
0x0DA7 => (Some(Consonant), None),
0x0DA8 => (Some(Consonant), None),
0x0DA9 => (Some(Consonant), None),
0x0DAA => (Some(Consonant), None),
0x0DAB => (Some(Consonant), None),
0x0DAC => (Some(Consonant), None),
0x0DAD => (Some(Consonant), None),
0x0DAE => (Some(Consonant), None),
0x0DAF => (Some(Consonant), None),
0x0DB0 => (Some(Consonant), None),
0x0DB1 => (Some(Consonant), None),
0x0DB2 => (None, None),
0x0DB3 => (Some(Consonant), None),
0x0DB4 => (Some(Consonant), None),
0x0DB5 => (Some(Consonant), None),
0x0DB6 => (Some(Consonant), None),
0x0DB7 => (Some(Consonant), None),
0x0DB8 => (Some(Consonant), None),
0x0DB9 => (Some(Consonant), None),
0x0DBA => (Some(Consonant), None),
0x0DBB => (Some(Consonant), None),
0x0DBC => (None, None),
0x0DBD => (Some(Consonant), None),
0x0DBE => (None, None),
0x0DBF => (None, None),
0x0DC0 => (Some(Consonant), None),
0x0DC1 => (Some(Consonant), None),
0x0DC2 => (Some(Consonant), None),
0x0DC3 => (Some(Consonant), None),
0x0DC4 => (Some(Consonant), None),
0x0DC5 => (Some(Consonant), None),
0x0DC6 => (Some(Consonant), None),
0x0DC7 => (None, None),
0x0DC8 => (None, None),
0x0DC9 => (None, None),
0x0DCA => (Some(Virama), Some(TopPosition)),
0x0DCB => (None, None),
0x0DCC => (None, None),
0x0DCD => (None, None),
0x0DCE => (None, None),
0x0DCF => (Some(VowelDependent), Some(RightPosition)),
0x0DD0 => (Some(VowelDependent), Some(RightPosition)),
0x0DD1 => (Some(VowelDependent), Some(RightPosition)),
0x0DD2 => (Some(VowelDependent), Some(TopPosition)),
0x0DD3 => (Some(VowelDependent), Some(TopPosition)),
0x0DD4 => (Some(VowelDependent), Some(BottomPosition)),
0x0DD5 => (None, None),
0x0DD6 => (Some(VowelDependent), Some(BottomPosition)),
0x0DD7 => (None, None),
0x0DD8 => (Some(VowelDependent), Some(RightPosition)),
0x0DD9 => (Some(VowelDependent), Some(LeftPosition)),
0x0DDA => (Some(VowelDependent), Some(TopAndLeftPosition)),
0x0DDB => (Some(VowelDependent), Some(LeftPosition)),
0x0DDC => (Some(VowelDependent), Some(LeftAndRightPosition)),
0x0DDD => (Some(VowelDependent), Some(TopLeftAndRightPosition)),
0x0DDE => (Some(VowelDependent), Some(LeftAndRightPosition)),
0x0DDF => (Some(VowelDependent), Some(RightPosition)),
0x0DE0 => (None, None),
0x0DE1 => (None, None),
0x0DE2 => (None, None),
0x0DE3 => (None, None),
0x0DE4 => (None, None),
0x0DE5 => (None, None),
0x0DE6 => (Some(Number), None),
0x0DE7 => (Some(Number), None),
0x0DE8 => (Some(Number), None),
0x0DE9 => (Some(Number), None),
0x0DEA => (Some(Number), None),
0x0DEB => (Some(Number), None),
0x0DEC => (Some(Number), None),
0x0DED => (Some(Number), None),
0x0DEE => (Some(Number), None),
0x0DEF => (Some(Number), None),
0x0DF0 => (None, None),
0x0DF1 => (None, None),
0x0DF2 => (Some(VowelDependent), Some(RightPosition)),
0x0DF3 => (Some(VowelDependent), Some(RightPosition)),
0x0DF4 => (None, None),
0x0DF5 => (None, None),
0x0DF6 => (None, None),
0x0DF7 => (None, None),
0x0DF8 => (None, None),
0x0DF9 => (None, None),
0x0DFA => (None, None),
0x0DFB => (None, None),
0x0DFC => (None, None),
0x0DFD => (None, None),
0x0DFE => (None, None),
0x0DFF => (None, None),
0x1CD0 => (Some(Cantillation), Some(TopPosition)),
0x1CD1 => (Some(Cantillation), Some(TopPosition)),
0x1CD2 => (Some(Cantillation), Some(TopPosition)),
0x1CD3 => (None, None),
0x1CD4 => (Some(Cantillation), Some(Overstruck)),
0x1CD5 => (Some(Cantillation), Some(BottomPosition)),
0x1CD6 => (Some(Cantillation), Some(BottomPosition)),
0x1CD7 => (Some(Cantillation), Some(BottomPosition)),
0x1CD8 => (Some(Cantillation), Some(BottomPosition)),
0x1CD9 => (Some(Cantillation), Some(BottomPosition)),
0x1CDA => (Some(Cantillation), Some(TopPosition)),
0x1CDB => (Some(Cantillation), Some(TopPosition)),
0x1CDC => (Some(Cantillation), Some(BottomPosition)),
0x1CDD => (Some(Cantillation), Some(BottomPosition)),
0x1CDE => (Some(Cantillation), Some(BottomPosition)),
0x1CDF => (Some(Cantillation), Some(BottomPosition)),
0x1CE0 => (Some(Cantillation), Some(TopPosition)),
0x1CE1 => (Some(Cantillation), Some(RightPosition)),
0x1CE2 => (Some(Avagraha), Some(Overstruck)),
0x1CE3 => (None, Some(Overstruck)),
0x1CE4 => (None, Some(Overstruck)),
0x1CE5 => (None, Some(Overstruck)),
0x1CE6 => (None, Some(Overstruck)),
0x1CE7 => (None, Some(Overstruck)),
0x1CE8 => (Some(Avagraha), Some(Overstruck)),
0x1CE9 => (Some(Symbol), None),
0x1CEA => (None, None),
0x1CEB => (None, None),
0x1CEC => (Some(Symbol), None),
0x1CED => (Some(Avagraha), Some(BottomPosition)),
0x1CEE => (Some(Symbol), None),
0x1CEF => (None, None),
0x1CF0 => (None, None),
0x1CF1 => (Some(Symbol), None),
0x1CF2 => (Some(Visarga), None),
0x1CF3 => (Some(Visarga), None),
0x1CF4 => (Some(Cantillation), Some(TopPosition)),
0x1CF5 => (Some(ConsonantWithStacker), None),
0x1CF6 => (Some(ConsonantWithStacker), None),
0x1CF7 => (None, None),
0x1CF8 => (Some(Cantillation), None),
0x1CF9 => (Some(Cantillation), None),
0xA8E0 => (Some(Cantillation), Some(TopPosition)),
0xA8E1 => (Some(Cantillation), Some(TopPosition)),
0xA8E2 => (Some(Cantillation), Some(TopPosition)),
0xA8E3 => (Some(Cantillation), Some(TopPosition)),
0xA8E4 => (Some(Cantillation), Some(TopPosition)),
0xA8E5 => (Some(Cantillation), Some(TopPosition)),
0xA8E6 => (Some(Cantillation), Some(TopPosition)),
0xA8E7 => (Some(Cantillation), Some(TopPosition)),
0xA8E8 => (Some(Cantillation), Some(TopPosition)),
0xA8E9 => (Some(Cantillation), Some(TopPosition)),
0xA8EA => (Some(Cantillation), Some(TopPosition)),
0xA8EB => (Some(Cantillation), Some(TopPosition)),
0xA8EC => (Some(Cantillation), Some(TopPosition)),
0xA8ED => (Some(Cantillation), Some(TopPosition)),
0xA8EE => (Some(Cantillation), Some(TopPosition)),
0xA8EF => (Some(Cantillation), Some(TopPosition)),
0xA8F0 => (Some(Cantillation), Some(TopPosition)),
0xA8F1 => (Some(Cantillation), Some(TopPosition)),
0xA8F2 => (Some(Bindu), None),
0xA8F3 => (Some(Bindu), None),
0xA8F4 => (None, None),
0xA8F5 => (None, None),
0xA8F6 => (None, None),
0xA8F7 => (None, None),
0xA8F8 => (None, None),
0xA8F9 => (None, None),
0xA8FA => (None, None),
0xA8FB => (None, None),
0xA8FC => (None, None),
0xA8FD => (None, None),
0xA8FE => (Some(VowelIndependent), None),
0xA8FF => (Some(VowelDependent), Some(TopPosition)),
0x111E0 => (None, None),
0x111E1 => (Some(Number), None),
0x111E2 => (Some(Number), None),
0x111E3 => (Some(Number), None),
0x111E4 => (Some(Number), None),
0x111E5 => (Some(Number), None),
0x111E6 => (Some(Number), None),
0x111E7 => (Some(Number), None),
0x111E8 => (Some(Number), None),
0x111E9 => (Some(Number), None),
0x111EA => (Some(Number), None),
0x111EB => (Some(Number), None),
0x111EC => (Some(Number), None),
0x111ED => (Some(Number), None),
0x111EE => (Some(Number), None),
0x111EF => (Some(Number), None),
0x111F0 => (Some(Number), None),
0x111F1 => (Some(Number), None),
0x111F2 => (Some(Number), None),
0x111F3 => (Some(Number), None),
0x111F4 => (Some(Number), None),
0x111F5 => (None, None),
0x111F6 => (None, None),
0x111F7 => (None, None),
0x111F8 => (None, None),
0x111F9 => (None, None),
0x111FA => (None, None),
0x111FB => (None, None),
0x111FC => (None, None),
0x111FD => (None, None),
0x111FE => (None, None),
0x111FF => (None, None),
0x11301 => (Some(Bindu), Some(TopPosition)),
0x11303 => (Some(Visarga), Some(RightPosition)),
0x1133B => (Some(Nukta), Some(BottomPosition)),
0x1133C => (Some(Nukta), Some(BottomPosition)),
0x00A0 => (Some(Placeholder), None),
0x00B2 => (Some(SyllableModifier), None),
0x00B3 => (Some(SyllableModifier), None),
0x200C => (Some(NonJoiner), None),
0x200D => (Some(Joiner), None),
0x2010 => (Some(Placeholder), None),
0x2011 => (Some(Placeholder), None),
0x2012 => (Some(Placeholder), None),
0x2013 => (Some(Placeholder), None),
0x2014 => (Some(Placeholder), None),
0x2074 => (Some(SyllableModifier), None),
0x2082 => (Some(SyllableModifier), None),
0x2083 => (Some(SyllableModifier), None),
0x2084 => (Some(SyllableModifier), None),
0x25CC => (Some(DottedCircle), None),
_ => (None, None),
}
}
#[cfg(test)]
mod tests {
use super::*;
mod matra_pos {
use super::*;
#[test]
fn test_no_canonical_decomposition_matra() {
assert_eq!(
matra_pos('\u{0AC9}', Script::Gujarati),
Some(Pos::AfterPost)
);
assert_eq!(matra_pos('\u{0B57}', Script::Oriya), Some(Pos::AfterPost));
}
#[test]
fn test_non_decomposed_matra() {
assert_eq!(matra_pos('\u{09CB}', Script::Bengali), None);
}
#[test]
fn test_non_matra() {
assert_eq!(matra_pos('\u{09B6}', Script::Bengali), None);
}
}
mod move_element {
use super::*;
#[test]
fn test_move_forward() {
let mut v = [1, 2, 3, 4];
move_element(&mut v, 0, 3);
assert_eq!([2, 3, 4, 1], v);
}
#[test]
fn test_move_backward() {
let mut v = [1, 2, 3, 4];
move_element(&mut v, 3, 1);
assert_eq!([1, 4, 2, 3], v);
}
}
mod constrain_vowel {
use super::*;
#[test]
fn test_insert_one_dotted_circle() {
let mut cs = vec!['\u{0909}', '\u{0941}'];
constrain_vowel(&mut cs);
assert_eq!(vec!['\u{0909}', '\u{25CC}', '\u{0941}'], cs);
}
#[test]
fn test_insert_two_dotted_circles() {
let mut cs = vec!['\u{0909}', '\u{0941}', '\u{090F}', '\u{0945}'];
constrain_vowel(&mut cs);
assert_eq!(
vec!['\u{0909}', '\u{25CC}', '\u{0941}', '\u{090F}', '\u{25CC}', '\u{0945}'],
cs
);
}
#[test]
fn test_insert_dotted_circle_after_reph() {
let mut cs = vec!['\u{0930}', '\u{094D}', '\u{0907}'];
constrain_vowel(&mut cs);
assert_eq!(vec!['\u{0930}', '\u{094D}', '\u{25CC}', '\u{0907}'], cs);
}
#[test]
fn test_should_not_insert_dotted_circle() {
let mut cs = vec!['\u{0930}', '\u{094D}'];
constrain_vowel(&mut cs);
assert_eq!(vec!['\u{0930}', '\u{094D}'], cs);
}
}
mod decompose_matra {
use super::*;
#[test]
fn test_single_decomposition() {
let mut cs = vec!['\u{09CB}'];
decompose_matra(&mut cs);
assert_eq!(vec!['\u{09C7}', '\u{09BE}'], cs);
}
#[test]
fn test_double_decomposition() {
let mut cs = vec!['\u{09CB}', '\u{09CB}'];
decompose_matra(&mut cs);
assert_eq!(vec!['\u{09C7}', '\u{09BE}', '\u{09C7}', '\u{09BE}'], cs);
}
}
mod recompose_bengali_ya_nukta {
use super::*;
#[test]
fn test_single_codepoint() {
let mut cs = vec!['\u{09AF}'];
recompose_bengali_ya_nukta(&mut cs);
assert_eq!(vec!['\u{09AF}'], cs);
}
#[test]
fn test_ya_nukta_ya() {
let mut cs = vec!['\u{09AF}', '\u{09BC}', '\u{09AF}'];
recompose_bengali_ya_nukta(&mut cs);
assert_eq!(vec!['\u{09DF}', '\u{09AF}'], cs);
}
#[test]
fn test_ya_ya_nukta() {
let mut cs = vec!['\u{09AF}', '\u{09AF}', '\u{09BC}'];
recompose_bengali_ya_nukta(&mut cs);
assert_eq!(vec!['\u{09AF}', '\u{09DF}'], cs);
}
}
mod reorder_marks {
use super::*;
const K: char = '\u{0915}';
const H: char = '\u{094D}';
const N: char = '\u{093C}';
const U: char = '\u{0951}';
#[test]
fn test_ka_nukta_halant_udatta() {
let mut cs = vec![K, N, H, U];
reorder_marks(&mut cs);
assert_eq!(vec![K, N, H, U], cs);
}
#[test]
fn test_ka_udatta_halant_nukta() {
let mut cs = vec![K, U, H, N];
reorder_marks(&mut cs);
assert_eq!(vec![K, N, H, U], cs);
}
#[test]
fn test_ka_halant_halant_nukta() {
let mut cs = vec![K, H, H, N];
reorder_marks(&mut cs);
assert_eq!(vec![K, N, H, H], cs);
}
#[test]
fn test_ka_halant_nukta_nukta() {
let mut cs = vec![K, H, N, N];
reorder_marks(&mut cs);
assert_eq!(vec![K, N, N, H], cs);
}
#[test]
fn test_ka_udatta_halant_nukta_x2() {
let mut cs = vec![K, U, H, N, K, U, H, N];
reorder_marks(&mut cs);
assert_eq!(vec![K, N, H, U, K, N, H, U], cs);
}
#[test]
fn test_beng_with_deva_udatta() {
let mut cs = vec!['\u{0995}', U, '\u{09CD}', '\u{09BC}'];
reorder_marks(&mut cs);
assert_eq!(vec!['\u{0995}', '\u{09BC}', '\u{09CD}', U], cs);
}
}
mod reorder_kannada_ra_halant_zwj {
use super::*;
const R: char = '\u{0CB0}';
const H: char = '\u{0CCD}';
const Z: char = '\u{200D}';
#[test]
fn test_ra_halant() {
let mut cs = vec![R, H];
reorder_kannada_ra_halant_zwj(&mut cs);
assert_eq!(vec![R, H], cs);
}
#[test]
fn test_ra_halant_zwj() {
let mut cs = vec![R, H, Z];
reorder_kannada_ra_halant_zwj(&mut cs);
assert_eq!(vec![R, Z, H], cs);
}
#[test]
fn test_non_initial_ra_halant_zwj() {
let mut cs = vec![R, H, R, H, Z];
reorder_kannada_ra_halant_zwj(&mut cs);
assert_eq!(vec![R, H, R, H, Z], cs);
}
}
}