#include "harfbuzz-shaper.h"
#include "harfbuzz-shaper-private.h"
#include <assert.h>
#include <stdio.h>
#define FLAG(x) (1 << (x))
static HB_Bool isLetter(HB_UChar16 ucs)
{
const int test = FLAG(HB_Letter_Uppercase) |
FLAG(HB_Letter_Lowercase) |
FLAG(HB_Letter_Titlecase) |
FLAG(HB_Letter_Modifier) |
FLAG(HB_Letter_Other);
return FLAG(HB_GetUnicodeCharCategory(ucs)) & test;
}
static HB_Bool isMark(HB_UChar16 ucs)
{
const int test = FLAG(HB_Mark_NonSpacing) |
FLAG(HB_Mark_SpacingCombining) |
FLAG(HB_Mark_Enclosing);
return FLAG(HB_GetUnicodeCharCategory(ucs)) & test;
}
enum Form {
Invalid = 0x0,
UnknownForm = Invalid,
Consonant,
Nukta,
Halant,
Matra,
VowelMark,
StressMark,
IndependentVowel,
LengthMark,
Control,
Other
};
static const unsigned char indicForms[0xe00-0x900] = {
Invalid, VowelMark, VowelMark, VowelMark,
IndependentVowel, IndependentVowel, IndependentVowel, IndependentVowel,
IndependentVowel, IndependentVowel, IndependentVowel, IndependentVowel,
IndependentVowel, IndependentVowel, IndependentVowel, IndependentVowel,
IndependentVowel, IndependentVowel, IndependentVowel, IndependentVowel,
IndependentVowel, Consonant, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Consonant, UnknownForm, UnknownForm,
Nukta, Other, Matra, Matra,
Matra, Matra, Matra, Matra,
Matra, Matra, Matra, Matra,
Matra, Matra, Matra, Matra,
Matra, Halant, UnknownForm, UnknownForm,
Other, StressMark, StressMark, StressMark,
StressMark, UnknownForm, UnknownForm, UnknownForm,
Consonant, Consonant, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
IndependentVowel, IndependentVowel, VowelMark, VowelMark,
Other, Other, Other, Other,
Other, Other, Other, Other,
Other, Other, Other, Other,
Other, Other, Other, Other,
Other, Other, Other, Other,
Other, Other, Other, Consonant,
Consonant, Consonant , Consonant, Consonant,
Invalid, VowelMark, VowelMark, VowelMark,
Invalid, IndependentVowel, IndependentVowel, IndependentVowel,
IndependentVowel, IndependentVowel, IndependentVowel, IndependentVowel,
IndependentVowel, Invalid, Invalid, IndependentVowel,
IndependentVowel, Invalid, Invalid, IndependentVowel,
IndependentVowel, Consonant, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Invalid, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Invalid, Consonant, Invalid,
Invalid, Invalid, Consonant, Consonant,
Consonant, Consonant, UnknownForm, UnknownForm,
Nukta, Other, Matra, Matra,
Matra, Matra, Matra, Matra,
Matra, Invalid, Invalid, Matra,
Matra, Invalid, Invalid, Matra,
Matra, Halant, Consonant, UnknownForm,
Invalid, Invalid, Invalid, Invalid,
Invalid, Invalid, Invalid, VowelMark,
Invalid, Invalid, Invalid, Invalid,
Consonant, Consonant, Invalid, Consonant,
IndependentVowel, IndependentVowel, VowelMark, VowelMark,
Other, Other, Other, Other,
Other, Other, Other, Other,
Other, Other, Other, Other,
Consonant, Consonant, Other, Other,
Other, Other, Other, Other,
Other, Other, Other, Other,
Other, Other, Other, Other,
Invalid, VowelMark, VowelMark, VowelMark,
Invalid, IndependentVowel, IndependentVowel, IndependentVowel,
IndependentVowel, IndependentVowel, IndependentVowel, Invalid,
Invalid, Invalid, Invalid, IndependentVowel,
IndependentVowel, Invalid, Invalid, IndependentVowel,
IndependentVowel, Consonant, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Invalid, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Invalid, Consonant, Consonant,
Invalid, Consonant, Consonant, Invalid,
Consonant, Consonant, UnknownForm, UnknownForm,
Nukta, Other, Matra, Matra,
Matra, Matra, Matra, Invalid,
Invalid, Invalid, Invalid, Matra,
Matra, Invalid, Invalid, Matra,
Matra, Halant, UnknownForm, UnknownForm,
Invalid, Invalid, Invalid, Invalid,
Invalid, UnknownForm, UnknownForm, UnknownForm,
Invalid, Consonant, Consonant, Consonant,
Consonant, Invalid, Consonant, Invalid,
Other, Other, Invalid, Invalid,
Other, Other, Other, Other,
Other, Other, Other, Other,
Other, Other, Other, Other,
StressMark, StressMark, Consonant, Consonant,
Other, Other, Other, Other,
Other, Other, Other, Other,
Other, Other, Other, Other,
Invalid, VowelMark, VowelMark, VowelMark,
Invalid, IndependentVowel, IndependentVowel, IndependentVowel,
IndependentVowel, IndependentVowel, IndependentVowel, IndependentVowel,
IndependentVowel, IndependentVowel, Invalid, IndependentVowel,
IndependentVowel, IndependentVowel, Invalid, IndependentVowel,
IndependentVowel, Consonant, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Invalid, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Invalid, Consonant, Consonant,
Invalid, Consonant, Consonant, Consonant,
Consonant, Consonant, UnknownForm, UnknownForm,
Nukta, Other, Matra, Matra,
Matra, Matra, Matra, Matra,
Matra, Matra, Invalid, Matra,
Matra, Matra, Invalid, Matra,
Matra, Halant, UnknownForm, UnknownForm,
Other, UnknownForm, UnknownForm, UnknownForm,
UnknownForm, UnknownForm, UnknownForm, UnknownForm,
UnknownForm, UnknownForm, UnknownForm, UnknownForm,
UnknownForm, UnknownForm, UnknownForm, UnknownForm,
IndependentVowel, IndependentVowel, VowelMark, VowelMark,
Other, Other, Other, Other,
Other, Other, Other, Other,
Other, Other, Other, Other,
Other, Other, Other, Other,
Other, Other, Other, Other,
Other, Other, Other, Other,
Other, Other, Other, Other,
Invalid, VowelMark, VowelMark, VowelMark,
Invalid, IndependentVowel, IndependentVowel, IndependentVowel,
IndependentVowel, IndependentVowel, IndependentVowel, IndependentVowel,
IndependentVowel, Invalid, Invalid, IndependentVowel,
IndependentVowel, Invalid, Invalid, IndependentVowel,
IndependentVowel, Consonant, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Invalid, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Invalid, Consonant, Consonant,
Invalid, Consonant, Consonant, Consonant,
Consonant, Consonant, UnknownForm, UnknownForm,
Nukta, Other, Matra, Matra,
Matra, Matra, Matra, Matra,
Invalid, Invalid, Invalid, Matra,
Matra, Invalid, Invalid, Matra,
Matra, Halant, UnknownForm, UnknownForm,
Other, Invalid, Invalid, Invalid,
Invalid, UnknownForm, LengthMark, LengthMark,
Invalid, Invalid, Invalid, Invalid,
Consonant, Consonant, Invalid, Consonant,
IndependentVowel, IndependentVowel, Invalid, Invalid,
Invalid, Invalid, Other, Other,
Other, Other, Other, Other,
Other, Other, Other, Other,
Other, Consonant, Other, Other,
Other, Other, Other, Other,
Other, Other, Other, Other,
Other, Other, Other, Other,
Invalid, Invalid, VowelMark, Other,
Invalid, IndependentVowel, IndependentVowel, IndependentVowel,
IndependentVowel, IndependentVowel, IndependentVowel, Invalid,
Invalid, Invalid, IndependentVowel, IndependentVowel,
IndependentVowel, Invalid, IndependentVowel, IndependentVowel,
IndependentVowel, Consonant, Invalid, Invalid,
Invalid, Consonant, Consonant, Invalid,
Consonant, Invalid, Consonant, Consonant,
Invalid, Invalid, Invalid, Consonant,
Consonant, Invalid, Invalid, Invalid,
Consonant, Consonant, Consonant, Invalid,
Invalid, Invalid, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Consonant, UnknownForm, UnknownForm,
Invalid, Invalid, Matra, Matra,
Matra, Matra, Matra, Invalid,
Invalid, Invalid, Matra, Matra,
Matra, Invalid, Matra, Matra,
Matra, Halant, Invalid, Invalid,
Invalid, Invalid, Invalid, Invalid,
Invalid, Invalid, Invalid, LengthMark,
Invalid, Invalid, Invalid, Invalid,
Invalid, Invalid, Invalid, Invalid,
Invalid, Invalid, Invalid, Invalid,
Invalid, Invalid, Other, Other,
Other, Other, Other, Other,
Other, Other, Other, Other,
Other, Other, Other, Other,
Other, Other, Other, Other,
Other, Other, Other, Other,
Other, Other, Other, Other,
Invalid, VowelMark, VowelMark, VowelMark,
Invalid, IndependentVowel, IndependentVowel, IndependentVowel,
IndependentVowel, IndependentVowel, IndependentVowel, IndependentVowel,
IndependentVowel, Invalid, IndependentVowel, IndependentVowel,
IndependentVowel, Invalid, IndependentVowel, IndependentVowel,
IndependentVowel, Consonant, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Invalid, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Invalid, Consonant, Consonant, Consonant,
Consonant, Consonant, UnknownForm, UnknownForm,
Invalid, Invalid, Matra, Matra,
Matra, Matra, Matra, Matra,
Matra, Invalid, Matra, Matra,
Matra, Invalid, Matra, Matra,
Matra, Halant, Invalid, Invalid,
Invalid, Invalid, Invalid, Invalid,
Invalid, LengthMark, Matra, Invalid,
Invalid, Invalid, Invalid, Invalid,
Invalid, Invalid, Invalid, Invalid,
IndependentVowel, IndependentVowel, Invalid, Invalid,
Invalid, Invalid, Other, Other,
Other, Other, Other, Other,
Other, Other, Other, Other,
Other, Other, Other, Other,
Other, Other, Other, Other,
Other, Other, Other, Other,
Other, Other, Other, Other,
Invalid, Invalid, VowelMark, VowelMark,
Invalid, IndependentVowel, IndependentVowel, IndependentVowel,
IndependentVowel, IndependentVowel, IndependentVowel, IndependentVowel,
IndependentVowel, Invalid, IndependentVowel, IndependentVowel,
IndependentVowel, Invalid, IndependentVowel, IndependentVowel,
IndependentVowel, Consonant, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Invalid, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Invalid, Consonant, Consonant, Consonant,
Consonant, Consonant, UnknownForm, UnknownForm,
Nukta, Other, Matra, Matra,
Matra, Matra, Matra, Matra,
Matra, Invalid, Matra, Matra,
Matra, Invalid, Matra, Matra,
Matra, Halant, Invalid, Invalid,
Invalid, Invalid, Invalid, Invalid,
Invalid, LengthMark, LengthMark, Invalid,
Invalid, Invalid, Invalid, Invalid,
Invalid, Invalid, Consonant, Invalid,
IndependentVowel, IndependentVowel, VowelMark, VowelMark,
Invalid, Invalid, Other, Other,
Other, Other, Other, Other,
Other, Other, Other, Other,
Other, Other, Other, Other,
Other, Other, Other, Other,
Other, Other, Other, Other,
Other, Other, Other, Other,
Invalid, Invalid, VowelMark, VowelMark,
Invalid, IndependentVowel, IndependentVowel, IndependentVowel,
IndependentVowel, IndependentVowel, IndependentVowel, IndependentVowel,
IndependentVowel, Invalid, IndependentVowel, IndependentVowel,
IndependentVowel, Invalid, IndependentVowel, IndependentVowel,
IndependentVowel, Consonant, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Invalid, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Consonant, UnknownForm, UnknownForm,
Invalid, Invalid, Matra, Matra,
Matra, Matra, Matra, Matra,
Invalid, Invalid, Matra, Matra,
Matra, Invalid, Matra, Matra,
Matra, Halant, Invalid, Invalid,
Invalid, Invalid, Invalid, Invalid,
Invalid, Invalid, Invalid, Matra,
Invalid, Invalid, Invalid, Invalid,
Invalid, Invalid, Invalid, Invalid,
IndependentVowel, IndependentVowel, Invalid, Invalid,
Invalid, Invalid, Other, Other,
Other, Other, Other, Other,
Other, Other, Other, Other,
Other, Other, Other, Other,
Other, Other, Other, Other,
Other, Other, Other, Other,
Other, Other, Other, Other,
Invalid, Invalid, VowelMark, VowelMark,
Invalid, IndependentVowel, IndependentVowel, IndependentVowel,
IndependentVowel, IndependentVowel, IndependentVowel, IndependentVowel,
IndependentVowel, IndependentVowel, IndependentVowel, IndependentVowel,
IndependentVowel, IndependentVowel, IndependentVowel, IndependentVowel,
IndependentVowel, IndependentVowel, IndependentVowel, Invalid,
Invalid, Invalid, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Consonant, Invalid, Consonant,
Consonant, Consonant, Consonant, Consonant,
Consonant, Consonant, Consonant, Consonant,
Invalid, Consonant, Invalid, Invalid,
Consonant, Consonant, Consonant, Consonant,
Consonant, Consonant, Consonant, Invalid,
Invalid, Invalid, Halant, Invalid,
Invalid, Invalid, Invalid, Matra,
Matra, Matra, Matra, Matra,
Matra, Invalid, Matra, Invalid,
Matra, Matra, Matra, Matra,
Matra, Matra, Matra, Matra,
Invalid, Invalid, Invalid, Invalid,
Invalid, Invalid, Invalid, Invalid,
Invalid, Invalid, Invalid, Invalid,
Invalid, Invalid, Invalid, Invalid,
Invalid, Invalid, Matra, Matra,
Other, Other, Other, Other,
Other, Other, Other, Other,
Other, Other, Other, Other,
};
enum Position {
None,
Pre,
Above,
Below,
Post,
Split,
Base,
Reph,
Vattu,
Inherit
};
static const unsigned char indicPosition[0xe00-0x900] = {
None, Above, Above, Post,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
Below, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, Post, Pre,
Post, Below, Below, Below,
Below, Above, Above, Above,
Above, Post, Post, Post,
Post, None, None, None,
None, Above, Below, Above,
Above, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, Below, Below,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, Above, Post, Post,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
Below, None, None, Post,
Below, None, None, None,
None, None, None, None,
None, None, None, None,
Below, None, Post, Pre,
Post, Below, Below, Below,
Below, None, None, Pre,
Pre, None, None, Split,
Split, Below, None, None,
None, None, None, None,
None, None, None, Post,
None, None, None, None,
None, None, None, None,
None, None, Below, Below,
None, None, None, None,
None, None, None, None,
None, None, None, None,
Below, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, Above, Above, Post,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, Post,
Below, None, None, None,
None, Below, None, None,
None, Below, None, None,
Below, None, Post, Pre,
Post, Below, Below, None,
None, None, None, Above,
Above, None, None, Above,
Above, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
Above, Above, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, Above, Above, Post,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
Below, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, Post, Pre,
Post, Below, Below, Below,
Below, Above, None, Above,
Above, Post, None, Post,
Post, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, Below, Below,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, Above, Post, Post,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
Below, None, None, None,
Below, None, None, None,
Below, Below, Below, Post,
Below, None, Below, Below,
None, None, None, None,
None, None, None, None,
None, None, Post, Above,
Post, Below, Below, Below,
None, None, None, Pre,
Split, None, None, Split,
Split, None, None, None,
None, None, None, None,
None, None, Above, Post,
None, None, None, None,
None, None, None, Post,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, Below, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, Above, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, Post, Post,
Above, Below, Below, None,
None, None, Pre, Pre,
Pre, None, Split, Split,
Split, Halant, None, None,
None, None, None, None,
None, None, None, Post,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, Post, Post, Post,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, Below, Below, Below,
Below, Below, Below, Below,
Below, Below, Below, Below,
Below, Below, Below, Below,
Below, Below, Below, Below,
Below, None, Below, Below,
Below, Below, Below, Below,
Below, None, Below, Below,
None, Below, Below, Below,
Below, Below, None, None,
None, None, Post, Above,
Above, Post, Post, Post,
Post, None, Above, Above,
Split, None, Post, Above,
Above, Halant, None, None,
None, None, None, None,
None, Above, Below, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, Post, Post,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, Below, Below, Below,
Below, Below, Below, Below,
Below, Below, Below, Below,
Below, Below, Below, Below,
Below, Below, Below, Below,
Below, Below, Below, Below,
Below, Below, Below, Below,
Below, None, Below, Below,
None, Below, Below, Below,
Below, Below, None, None,
None, None, Post, Above,
Split, Post, Post, Post,
Post, None, Above, Split,
Split, None, Split, Split,
Above, Halant, None, None,
None, None, None, None,
None, Post, Post, None,
None, None, None, None,
None, None, Below, None,
None, None, Below, Below,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, Post, Post,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, Post,
Post, None, Below, None,
None, Post, None, None,
None, None, None, None,
None, None, Post, Post,
Post, Post, Post, Post,
None, None, Pre, Pre,
Pre, None, Split, Split,
Split, Halant, None, None,
None, None, None, None,
None, None, None, Post,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, Post, Post,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, Post,
Post, Post, Above, Above,
Below, None, Below, None,
Post, Pre, Split, Pre,
Split, Split, Split, Post,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, Post, Post,
None, None, None, None,
None, None, None, None,
None, None, None, None
};
static inline Form form(unsigned short uc) {
if (uc < 0x900 || uc > 0xdff) {
if (uc == 0x25cc)
return Consonant;
if (uc == 0x200c || uc == 0x200d)
return Control;
return Other;
}
return (Form)indicForms[uc-0x900];
}
static inline Position indic_position(unsigned short uc) {
if (uc < 0x900 || uc > 0xdff)
return None;
return (Position) indicPosition[uc-0x900];
}
enum IndicScriptProperties {
HasReph = 0x01,
HasSplit = 0x02
};
const hb_uint8 scriptProperties[10] = {
HasReph,
HasReph|HasSplit,
0,
HasReph,
HasReph|HasSplit,
HasSplit,
HasSplit,
HasSplit|HasReph,
HasSplit,
HasSplit
};
struct IndicOrdering {
Form form;
Position position;
};
static const IndicOrdering devanagari_order [] = {
{ Consonant, Below },
{ Matra, Below },
{ VowelMark, Below },
{ StressMark, Below },
{ Matra, Above },
{ Matra, Post },
{ Consonant, Reph },
{ VowelMark, Above },
{ StressMark, Above },
{ VowelMark, Post },
{ (Form)0, None }
};
static const IndicOrdering bengali_order [] = {
{ Consonant, Below },
{ Matra, Below },
{ Matra, Above },
{ Consonant, Reph },
{ VowelMark, Above },
{ Consonant, Post },
{ Matra, Post },
{ VowelMark, Post },
{ (Form)0, None }
};
static const IndicOrdering gurmukhi_order [] = {
{ Consonant, Below },
{ Matra, Below },
{ Matra, Above },
{ Consonant, Post },
{ Matra, Post },
{ VowelMark, Above },
{ (Form)0, None }
};
static const IndicOrdering tamil_order [] = {
{ Matra, Above },
{ Matra, Post },
{ VowelMark, Post },
{ (Form)0, None }
};
static const IndicOrdering telugu_order [] = {
{ Matra, Above },
{ Matra, Below },
{ Matra, Post },
{ Consonant, Below },
{ Consonant, Post },
{ VowelMark, Post },
{ (Form)0, None }
};
static const IndicOrdering kannada_order [] = {
{ Matra, Above },
{ Matra, Post },
{ Consonant, Below },
{ Consonant, Post },
{ LengthMark, Post },
{ Consonant, Reph },
{ VowelMark, Post },
{ (Form)0, None }
};
static const IndicOrdering malayalam_order [] = {
{ Consonant, Below },
{ Matra, Below },
{ Consonant, Reph },
{ Consonant, Post },
{ Matra, Post },
{ VowelMark, Post },
{ (Form)0, None }
};
static const IndicOrdering sinhala_order [] = {
{ Matra, Below },
{ Matra, Above },
{ Matra, Post },
{ VowelMark, Post },
{ (Form)0, None }
};
static const IndicOrdering * const indic_order[] = {
devanagari_order, bengali_order, gurmukhi_order, devanagari_order, bengali_order, tamil_order, telugu_order, kannada_order, malayalam_order, sinhala_order };
static const unsigned short split_matras[] = {
0x9cb, 0x9c7, 0x9be, 0x0,
0x9cc, 0x9c7, 0x9d7, 0x0,
0xb48, 0xb47, 0xb56, 0x0,
0xb4b, 0xb47, 0xb3e, 0x0,
0xb4c, 0xb47, 0xb57, 0x0,
0xbca, 0xbc6, 0xbbe, 0x0,
0xbcb, 0xbc7, 0xbbe, 0x0,
0xbcc, 0xbc6, 0xbd7, 0x0,
0xc48, 0xc46, 0xc56, 0x0,
0xcc0, 0xcbf, 0xcd5, 0x0,
0xcc7, 0xcc6, 0xcd5, 0x0,
0xcc8, 0xcc6, 0xcd6, 0x0,
0xcca, 0xcc6, 0xcc2, 0x0,
0xccb, 0xcc6, 0xcc2, 0xcd5,
0xd4a, 0xd46, 0xd3e, 0x0,
0xd4b, 0xd47, 0xd3e, 0x0,
0xd4c, 0xd46, 0xd57, 0x0,
0xdda, 0xdd9, 0xdca, 0x0,
0xddc, 0xdd9, 0xdcf, 0x0,
0xddd, 0xdd9, 0xdcf, 0xdca,
0xdde, 0xdd9, 0xddf, 0x0,
0xffff
};
static inline void splitMatra(unsigned short *reordered, int matra, int &len)
{
unsigned short matra_uc = reordered[matra];
const unsigned short *split = split_matras;
while (split[0] < matra_uc)
split += 4;
assert(*split == matra_uc);
++split;
int added_chars = split[2] == 0x0 ? 1 : 2;
memmove(reordered + matra + added_chars, reordered + matra, (len-matra)*sizeof(unsigned short));
reordered[matra] = split[0];
reordered[matra+1] = split[1];
if(added_chars == 2)
reordered[matra+2] = split[2];
len += added_chars;
}
#ifndef NO_OPENTYPE
static const HB_OpenTypeFeature indic_features[] = {
{ HB_MAKE_TAG('c', 'c', 'm', 'p'), CcmpProperty },
{ HB_MAKE_TAG('i', 'n', 'i', 't'), InitProperty },
{ HB_MAKE_TAG('n', 'u', 'k', 't'), NuktaProperty },
{ HB_MAKE_TAG('a', 'k', 'h', 'n'), AkhantProperty },
{ HB_MAKE_TAG('r', 'p', 'h', 'f'), RephProperty },
{ HB_MAKE_TAG('b', 'l', 'w', 'f'), BelowFormProperty },
{ HB_MAKE_TAG('h', 'a', 'l', 'f'), HalfFormProperty },
{ HB_MAKE_TAG('p', 's', 't', 'f'), PostFormProperty },
{ HB_MAKE_TAG('v', 'a', 't', 'u'), VattuProperty },
{ HB_MAKE_TAG('p', 'r', 'e', 's'), PreSubstProperty },
{ HB_MAKE_TAG('b', 'l', 'w', 's'), BelowSubstProperty },
{ HB_MAKE_TAG('a', 'b', 'v', 's'), AboveSubstProperty },
{ HB_MAKE_TAG('p', 's', 't', 's'), PostSubstProperty },
{ HB_MAKE_TAG('h', 'a', 'l', 'n'), HalantProperty },
{ 0, 0 }
};
#endif
#ifdef INDIC_DEBUG
#define IDEBUG hb_debug
#include <stdarg.h>
static void hb_debug(const char *msg, ...)
{
va_list ap;
va_start(ap, msg); vfprintf(stderr, msg, ap);
va_end(ap);
fprintf(stderr, "\n");
}
#else
#define IDEBUG if(0) printf
#endif
#if 0#endif
static bool indic_shape_syllable(HB_Bool openType, HB_ShaperItem *item, bool invalid)
{
HB_Script script = item->item.script;
assert(script >= HB_Script_Devanagari && script <= HB_Script_Sinhala);
const unsigned short script_base = 0x0900 + 0x80*(script-HB_Script_Devanagari);
const unsigned short ra = script_base + 0x30;
const unsigned short halant = script_base + 0x4d;
const unsigned short nukta = script_base + 0x3c;
bool control = false;
int len = (int)item->item.length;
IDEBUG(">>>>> indic shape: from=%d, len=%d invalid=%d", item->item.pos, item->item.length, invalid);
if ((int)item->num_glyphs < len+4) {
item->num_glyphs = len+4;
return false;
}
HB_STACKARRAY(HB_UChar16, reordered, len + 4);
HB_STACKARRAY(hb_uint8, position, len + 4);
unsigned char properties = scriptProperties[script-HB_Script_Devanagari];
if (invalid) {
*reordered = 0x25cc;
memcpy(reordered+1, item->string + item->item.pos, len*sizeof(HB_UChar16));
len++;
} else {
memcpy(reordered, item->string + item->item.pos, len*sizeof(HB_UChar16));
}
if (reordered[len-1] == 0x200c) len--;
int i;
int base = 0;
int reph = -1;
#ifdef INDIC_DEBUG
IDEBUG("original:");
for (i = 0; i < len; i++) {
IDEBUG(" %d: %4x", i, reordered[i]);
}
#endif
if (len != 1) {
HB_UChar16 *uc = reordered;
bool beginsWithRa = false;
if (form(*uc) == Consonant || (script == HB_Script_Bengali && form(*uc) == IndependentVowel)) {
if ((properties & HasReph) && (len > 2) &&
(*uc == ra || *uc == 0x9f0) && *(uc+1) == halant)
beginsWithRa = true;
if (beginsWithRa && form(*(uc+2)) == Control)
beginsWithRa = false;
base = (beginsWithRa ? 2 : 0);
IDEBUG(" length = %d, beginsWithRa = %d, base=%d", len, beginsWithRa, base);
int lastConsonant = 0;
int matra = -1;
memset(position, 0, len);
if (script == HB_Script_Devanagari || script == HB_Script_Gujarati) {
bool vattu = false;
for (i = base; i < len; ++i) {
position[i] = form(uc[i]);
if (position[i] == Consonant) {
lastConsonant = i;
vattu = (!vattu && uc[i] == ra);
if (vattu) {
IDEBUG("excluding vattu glyph at %d from base candidates", i);
position[i] = Vattu;
}
} else if (position[i] == Matra) {
matra = i;
}
}
} else {
for (i = base; i < len; ++i) {
position[i] = form(uc[i]);
if (position[i] == Consonant)
lastConsonant = i;
else if (matra < 0 && position[i] == Matra)
matra = i;
}
}
int skipped = 0;
Position pos = Post;
for (i = len-1; i > base; i--) {
if (position[i] != Consonant && (position[i] != Control || script == HB_Script_Kannada))
continue;
Position charPosition = indic_position(uc[i]);
if (pos == Post && charPosition == Post) {
pos = Post;
} else if ((pos == Post || pos == Below) && charPosition == Below) {
if (script == HB_Script_Devanagari || script == HB_Script_Gujarati)
base = i;
pos = Below;
} else {
base = i;
break;
}
if (skipped == 2 && (script == HB_Script_Kannada || script == HB_Script_Telugu)) {
base = i;
break;
}
++skipped;
}
IDEBUG(" base consonant at %d skipped=%d, lastConsonant=%d", base, skipped, lastConsonant);
if (lastConsonant > base) {
int halantPos = 0;
if (uc[base+1] == halant)
halantPos = base + 1;
else if (uc[base+1] == nukta && uc[base+2] == halant)
halantPos = base + 2;
if (halantPos > 0) {
IDEBUG(" moving halant from %d to %d!", base+1, lastConsonant);
for (i = halantPos; i < lastConsonant; i++)
uc[i] = uc[i+1];
uc[lastConsonant] = halant;
}
}
Position matra_position = None;
if (matra > 0)
matra_position = indic_position(uc[matra]);
IDEBUG(" matra at %d with form %d, base=%d", matra, matra_position, base);
if (beginsWithRa && base != 0) {
int toPos = base+1;
if (toPos < len && uc[toPos] == nukta)
toPos++;
if (toPos < len && uc[toPos] == halant)
toPos++;
if (toPos < len && uc[toPos] == 0x200d)
toPos++;
if (toPos < len-1 && uc[toPos] == ra && uc[toPos+1] == halant)
toPos += 2;
if (script == HB_Script_Devanagari || script == HB_Script_Gujarati || script == HB_Script_Bengali) {
if (matra_position == Post || matra_position == Split) {
toPos = matra+1;
matra -= 2;
}
} else if (script == HB_Script_Kannada) {
toPos = len;
matra -= 2;
}
IDEBUG("moving leading ra+halant to position %d", toPos);
for (i = 2; i < toPos; i++)
uc[i-2] = uc[i];
uc[toPos-2] = ra;
uc[toPos-1] = halant;
base -= 2;
if (properties & HasReph)
reph = toPos-2;
}
if (matra_position == Split) {
splitMatra(uc, matra, len);
matra_position = indic_position(uc[matra]);
}
if (matra_position == Pre) {
unsigned short m = uc[matra];
while (matra--)
uc[matra+1] = uc[matra];
uc[0] = m;
base++;
}
}
for (i = 0; i < base; ++i)
position[i] = Pre;
position[base] = Base;
for (i = base+1; i < len; ++i) {
position[i] = indic_position(uc[i]);
if (uc[i] == nukta || uc[i] == halant)
position[i] = Inherit;
}
if (reph > 0) {
for (i = base+1; i < len; ++i)
if (uc[i] == ra)
reph = i;
position[reph] = Reph;
position[reph+1] = Inherit;
}
int fixed = base+1;
if (fixed < len && uc[fixed] == nukta)
fixed++;
if (fixed < len && uc[fixed] == halant)
fixed++;
if (fixed < len && uc[fixed] == 0x200d)
fixed++;
#ifdef INDIC_DEBUG
for (i = fixed; i < len; ++i)
IDEBUG("position[%d] = %d, form=%d uc=%x", i, position[i], form(uc[i]), uc[i]);
#endif
const IndicOrdering *finalOrder = indic_order[script-HB_Script_Devanagari];
IDEBUG(" reordering pass:");
IDEBUG(" base=%d fixed=%d", base, fixed);
int toMove = 0;
while (finalOrder[toMove].form && fixed < len-1) {
IDEBUG(" fixed = %d, toMove=%d, moving form %d with pos %d", fixed, toMove, finalOrder[toMove].form, finalOrder[toMove].position);
for (i = fixed; i < len; i++) {
if (form(uc[i]) == finalOrder[toMove].form &&
position[i] == finalOrder[toMove].position) {
int to = fixed;
if (i < len-1 && position[i+1] == Inherit) {
IDEBUG(" moving two chars from %d to %d", i, to);
unsigned short ch = uc[i];
unsigned short ch2 = uc[i+1];
unsigned char pos = position[i];
for (int j = i+1; j > to+1; j--) {
uc[j] = uc[j-2];
position[j] = position[j-2];
}
uc[to] = ch;
uc[to+1] = ch2;
position[to] = pos;
position[to+1] = pos;
fixed += 2;
} else {
IDEBUG(" moving one char from %d to %d", i, to);
unsigned short ch = uc[i];
unsigned char pos = position[i];
for (int j = i; j > to; j--) {
uc[j] = uc[j-1];
position[j] = position[j-1];
}
uc[to] = ch;
position[to] = pos;
fixed++;
}
}
}
toMove++;
}
}
if (reph > 0) {
for (i = base+1; i < len; ++i)
if (reordered[i] == ra)
reph = i;
}
#ifndef NO_OPENTYPE
const int availableGlyphs = item->num_glyphs;
#endif
if (!item->font->klass->convertStringToGlyphIndices(item->font,
reordered, len,
item->glyphs, &item->num_glyphs,
item->item.bidiLevel % 2))
goto error;
IDEBUG(" base=%d, reph=%d", base, reph);
IDEBUG("reordered:");
for (i = 0; i < len; i++) {
item->attributes[i].mark = false;
item->attributes[i].clusterStart = false;
item->attributes[i].justification = 0;
item->attributes[i].zeroWidth = false;
IDEBUG(" %d: %4x", i, reordered[i]);
}
for (i = 0; i < len; ++i)
control |= (form(reordered[i]) == Control);
#ifndef NO_OPENTYPE
if (openType) {
HB_STACKARRAY(unsigned short, clusters, len);
HB_STACKARRAY(unsigned int, properties, len);
for (i = 0; i < len; ++i)
clusters[i] = i;
for (i = 0; i < len; ++i)
properties[i] = ~(CcmpProperty
| NuktaProperty
| VattuProperty
| PreSubstProperty
| BelowSubstProperty
| AboveSubstProperty
| PostSubstProperty
| HalantProperty
| PositioningProperties);
if (item->item.pos == 0
|| !(isLetter(item->string[item->item.pos-1]) || isMark(item->string[item->item.pos-1])))
properties[0] &= ~InitProperty;
for (i = 0; i <= base; ++i)
properties[i] &= ~AkhantProperty;
if (reph >= 0) {
properties[reph] &= ~RephProperty;
properties[reph+1] &= ~RephProperty;
}
for (i = base+1; i < len; ++i)
properties[i] &= ~BelowFormProperty;
if (script == HB_Script_Devanagari || script == HB_Script_Gujarati) {
bool vattu = false;
for (i = base-2; i > 1; --i) {
if (form(reordered[i]) == Consonant) {
vattu = (!vattu && reordered[i] == ra);
if (vattu) {
IDEBUG("forming vattu ligature at %d", i);
properties[i] &= ~BelowFormProperty;
properties[i+1] &= ~BelowFormProperty;
}
}
}
}
for (i = 0; i < base; ++i)
properties[i] &= ~HalfFormProperty;
if (control) {
for (i = 2; i < len; ++i) {
if (reordered[i] == 0x200d ) {
properties[i-1] &= ~HalfFormProperty;
properties[i-2] &= ~HalfFormProperty;
} else if (reordered[i] == 0x200c ) {
properties[i-1] &= ~HalfFormProperty;
properties[i-2] &= ~HalfFormProperty;
}
}
}
for (i = base+1; i < len; ++i)
properties[i] &= ~PostFormProperty;
#ifdef INDIC_DEBUG
#endif
item->log_clusters = clusters;
HB_OpenTypeShape(item, properties);
int newLen = item->face->buffer->in_length;
HB_GlyphItem otl_glyphs = item->face->buffer->in_string;
if ((script == HB_Script_Malayalam || script == HB_Script_Tamil) && (form(reordered[0]) == Matra)) {
int basePos = 0;
while (basePos < newLen && (int)otl_glyphs[basePos].cluster <= base)
basePos++;
--basePos;
if (basePos < newLen && basePos > 1) {
HB_GlyphItemRec m = otl_glyphs[0];
--basePos;
for (i = 0; i < basePos; ++i)
otl_glyphs[i] = otl_glyphs[i+1];
otl_glyphs[basePos] = m;
}
}
HB_Bool positioned = HB_OpenTypePosition(item, availableGlyphs, false);
HB_FREE_STACKARRAY(clusters);
HB_FREE_STACKARRAY(properties);
if (!positioned)
goto error;
if (control) {
IDEBUG("found a control char in the syllable");
hb_uint32 i = 0, j = 0;
while (i < item->num_glyphs) {
if (form(reordered[otl_glyphs[i].cluster]) == Control) {
++i;
if (i >= item->num_glyphs)
break;
}
item->glyphs[j] = item->glyphs[i];
item->attributes[j] = item->attributes[i];
++i;
++j;
}
item->num_glyphs = j;
}
} else {
HB_HeuristicPosition(item);
}
#endif item->attributes[0].clusterStart = true;
HB_FREE_STACKARRAY(reordered);
HB_FREE_STACKARRAY(position);
IDEBUG("<<<<<<");
return true;
error:
HB_FREE_STACKARRAY(reordered);
HB_FREE_STACKARRAY(position);
return false;
}
static int indic_nextSyllableBoundary(HB_Script script, const HB_UChar16 *s, int start, int end, bool *invalid)
{
*invalid = false;
IDEBUG("indic_nextSyllableBoundary: start=%d, end=%d", start, end);
const HB_UChar16 *uc = s+start;
int pos = 0;
Form state = form(uc[pos]);
IDEBUG("state[%d]=%d (uc=%4x)", pos, state, uc[pos]);
pos++;
if (state != Consonant && state != IndependentVowel) {
if (state != Other)
*invalid = true;
goto finish;
}
while (pos < end - start) {
Form newState = form(uc[pos]);
IDEBUG("state[%d]=%d (uc=%4x)", pos, newState, uc[pos]);
switch(newState) {
case Control:
newState = state;
if (state == Halant && uc[pos] == 0x200d )
break;
++pos;
goto finish;
case Consonant:
if (state == Halant && (script != HB_Script_Sinhala || uc[pos-1] == 0x200d ))
break;
goto finish;
case Halant:
if (state == Nukta || state == Consonant)
break;
if (script == HB_Script_Bengali && pos == 1 &&
(uc[0] == 0x0985 || uc[0] == 0x098f))
break;
if (script == HB_Script_Sinhala && state == Matra) {
++pos;
continue;
}
if (script == HB_Script_Malayalam && state == Matra && uc[pos-1] == 0x0d41) {
++pos;
continue;
}
goto finish;
case Nukta:
if (state == Consonant)
break;
goto finish;
case StressMark:
if (state == VowelMark)
break;
case VowelMark:
if (state == Matra || state == LengthMark || state == IndependentVowel)
break;
case Matra:
if (state == Consonant || state == Nukta)
break;
if (state == Matra) {
break;
}
if (script == HB_Script_Bengali && uc[pos] == 0x9be && uc[pos-1] == 0x985)
break;
if (script == HB_Script_Tamil && state == Matra) {
if (uc[pos-1] == 0x0bc6 &&
(uc[pos] == 0xbbe || uc[pos] == 0xbd7))
break;
if (uc[pos-1] == 0x0bc7 && uc[pos] == 0xbbe)
break;
}
goto finish;
case LengthMark:
if (state == Matra) {
break;
}
case IndependentVowel:
case Invalid:
case Other:
goto finish;
}
state = newState;
pos++;
}
finish:
return pos+start;
}
HB_Bool HB_IndicShape(HB_ShaperItem *item)
{
assert(item->item.script >= HB_Script_Devanagari && item->item.script <= HB_Script_Sinhala);
HB_Bool openType = false;
#ifndef NO_OPENTYPE
openType = HB_SelectScript(item, indic_features);
#endif
unsigned short *logClusters = item->log_clusters;
HB_ShaperItem syllable = *item;
int first_glyph = 0;
int sstart = item->item.pos;
int end = sstart + item->item.length;
IDEBUG("indic_shape: from %d length %d", item->item.pos, item->item.length);
while (sstart < end) {
bool invalid;
int send = indic_nextSyllableBoundary(item->item.script, item->string, sstart, end, &invalid);
IDEBUG("syllable from %d, length %d, invalid=%s", sstart, send-sstart,
invalid ? "true" : "false");
syllable.item.pos = sstart;
syllable.item.length = send-sstart;
syllable.glyphs = item->glyphs + first_glyph;
syllable.attributes = item->attributes + first_glyph;
syllable.offsets = item->offsets + first_glyph;
syllable.advances = item->advances + first_glyph;
syllable.num_glyphs = item->num_glyphs - first_glyph;
if (!indic_shape_syllable(openType, &syllable, invalid)) {
IDEBUG("syllable shaping failed, syllable requests %d glyphs", syllable.num_glyphs);
item->num_glyphs += syllable.num_glyphs;
return false;
}
IDEBUG("syllable:");
hb_uint32 g;
for (g = first_glyph; g < first_glyph + syllable.num_glyphs; ++g)
IDEBUG(" %d -> glyph %x", g, item->glyphs[g]);
IDEBUG(" logclusters:");
int i;
for (i = sstart; i < send; ++i) {
IDEBUG(" %d -> glyph %d", i, first_glyph);
logClusters[i-item->item.pos] = first_glyph;
}
sstart = send;
first_glyph += syllable.num_glyphs;
}
item->num_glyphs = first_glyph;
return true;
}
void HB_IndicAttributes(HB_Script script, const HB_UChar16 *text, hb_uint32 from, hb_uint32 len, HB_CharAttributes *attributes)
{
int end = from + len;
const HB_UChar16 *uc = text + from;
attributes += from;
hb_uint32 i = 0;
while (i < len) {
bool invalid;
hb_uint32 boundary = indic_nextSyllableBoundary(script, text, from+i, end, &invalid) - from;
attributes[i].charStop = true;
if (boundary > len-1) boundary = len;
i++;
while (i < boundary) {
attributes[i].charStop = false;
++uc;
++i;
}
assert(i == boundary);
}
}