#include "harfbuzz-shaper.h"
#include "harfbuzz-shaper-private.h"
#include <assert.h>
static const HB_UChar16 ReplacementCharacter = 0xfffd;
typedef struct {
unsigned char shape;
unsigned char justification;
} HB_ArabicProperties;
typedef enum {
XIsolated,
XFinal,
XInitial,
XMedial,
XCausing
} ArabicShape;
typedef enum {
ArabicNone,
ArabicSpace,
Transparent,
Center,
Kashida,
Beh,
Noon,
Meem = Noon,
Heh = Noon,
KnottedHeh = Noon,
HehGoal = Noon,
SwashKaf = Noon,
Yeh,
Hah,
Seen,
Sad = Seen,
Tah,
Kaf = Tah,
Gaf = Tah,
Lam = Tah,
Ain,
Feh = Ain,
Qaf = Ain,
Alef,
Waw,
Dal,
TehMarbuta = Dal,
Reh,
HamzaOnHehGoal,
YehWithTail = HamzaOnHehGoal,
YehBarre = HamzaOnHehGoal,
Beth = Beh,
Gamal = Ain,
Heth = Noon,
Teth = Hah,
Yudh = Noon,
Kaph = Noon,
Lamadh = Lam,
Mim = Noon,
Nun = Noon,
Semakh = Noon,
FinalSemakh = Noon,
SyriacE = Ain,
Pe = Ain,
ReversedPe = Hah,
Qaph = Noon,
Shin = Noon,
Fe = Ain,
Alaph = Alef,
Dalath = Dal,
He = Dal,
SyriacWaw = Waw,
Zain = Alef,
YudhHe = Waw,
Sadhe = HamzaOnHehGoal,
Taw = Dal,
Dummy = HamzaOnHehGoal,
ArabicGroupsEnd
} ArabicGroup;
static const unsigned char arabic_group[0x150] = {
ArabicNone, ArabicNone, ArabicNone, ArabicNone,
ArabicNone, ArabicNone, ArabicNone, ArabicNone,
ArabicNone, ArabicNone, ArabicNone, ArabicNone,
ArabicNone, ArabicNone, ArabicNone, ArabicNone,
Transparent, Transparent, Transparent, Transparent,
Transparent, Transparent, ArabicNone, ArabicNone,
ArabicNone, ArabicNone, ArabicNone, ArabicNone,
ArabicNone, ArabicNone, ArabicNone, ArabicNone,
ArabicNone, ArabicNone, Alef, Alef,
Waw, Alef, Yeh, Alef,
Beh, TehMarbuta, Beh, Beh,
Hah, Hah, Hah, Dal,
Dal, Reh, Reh, Seen,
Seen, Sad, Sad, Tah,
Tah, Ain, Ain, ArabicNone,
ArabicNone, ArabicNone, ArabicNone, ArabicNone,
Kashida, Feh, Qaf, Kaf,
Lam, Meem, Noon, Heh,
Waw, Yeh, Yeh, Transparent,
Transparent, Transparent, Transparent, Transparent,
Transparent, Transparent, Transparent, Transparent,
Transparent, Transparent, Transparent, Transparent,
Transparent, ArabicNone, ArabicNone, ArabicNone,
ArabicNone, ArabicNone, ArabicNone, ArabicNone,
ArabicNone, ArabicNone, ArabicNone, ArabicNone,
ArabicNone, ArabicNone, ArabicNone, ArabicNone,
ArabicNone, ArabicNone, ArabicNone, ArabicNone,
ArabicNone, ArabicNone, Beh, Qaf,
Transparent, Alef, Alef, Alef,
ArabicNone, Alef, Waw, Waw,
Yeh, Beh, Beh, Beh,
Beh, Beh, Beh, Beh,
Beh, Hah, Hah, Hah,
Hah, Hah, Hah, Hah,
Dal, Dal, Dal, Dal,
Dal, Dal, Dal, Dal,
Dal, Reh, Reh, Reh,
Reh, Reh, Reh, Reh,
Reh, Reh, Seen, Seen,
Seen, Sad, Sad, Tah,
Ain, Feh, Feh, Feh,
Feh, Feh, Feh, Qaf,
Qaf, Gaf, SwashKaf, Gaf,
Kaf, Kaf, Kaf, Gaf,
Gaf, Gaf, Gaf, Gaf,
Gaf, Lam, Lam, Lam,
Lam, Noon, Noon, Noon,
Noon, Noon, KnottedHeh, Hah,
TehMarbuta, HehGoal, HamzaOnHehGoal, HamzaOnHehGoal,
Waw, Waw, Waw, Waw,
Waw, Waw, Waw, Waw,
Yeh, YehWithTail, Yeh, Waw,
Yeh, Yeh, YehBarre, YehBarre,
ArabicNone, TehMarbuta, Transparent, Transparent,
Transparent, Transparent, Transparent, Transparent,
Transparent, ArabicNone, ArabicNone, Transparent,
Transparent, Transparent, Transparent, Transparent,
Transparent, ArabicNone, ArabicNone, Transparent,
Transparent, ArabicNone, Transparent, Transparent,
Transparent, Transparent, Dal, Reh,
ArabicNone, ArabicNone, ArabicNone, ArabicNone,
ArabicNone, ArabicNone, ArabicNone, ArabicNone,
ArabicNone, ArabicNone, Seen, Sad,
Ain, ArabicNone, ArabicNone, KnottedHeh,
ArabicNone, ArabicNone, ArabicNone, ArabicNone,
ArabicNone, ArabicNone, ArabicNone, ArabicNone,
ArabicNone, ArabicNone, ArabicNone, ArabicNone,
ArabicNone, ArabicNone, ArabicNone, ArabicNone,
Alaph, Transparent, Beth, Gamal,
Gamal, Dalath, Dalath, He,
SyriacWaw, Zain, Heth, Teth,
Teth, Yudh, YudhHe, Kaph,
Lamadh, Mim, Nun, Semakh,
FinalSemakh, SyriacE, Pe, ReversedPe,
Sadhe, Qaph, Dalath, Shin,
Taw, Beth, Gamal, Dalath,
Transparent, Transparent, Transparent, Transparent,
Transparent, Transparent, Transparent, Transparent,
Transparent, Transparent, Transparent, Transparent,
Transparent, Transparent, Transparent, Transparent,
Transparent, Transparent, Transparent, Transparent,
Transparent, Transparent, Transparent, Transparent,
Transparent, Transparent, Transparent, ArabicNone,
ArabicNone, Zain, Kaph, Fe,
};
static ArabicGroup arabicGroup(unsigned short uc)
{
if (uc >= 0x0600 && uc < 0x750)
return (ArabicGroup) arabic_group[uc-0x600];
else if (uc == 0x200d)
return Center;
else if (HB_GetUnicodeCharCategory(uc) == HB_Separator_Space)
return ArabicSpace;
else
return ArabicNone;
}
typedef enum {
JNone,
JCausing,
JDual,
JRight,
JTransparent
} Joining;
static const Joining joining_for_group[ArabicGroupsEnd] = {
JNone,
JNone,
JTransparent,
JCausing,
JCausing,
JDual,
JDual,
JDual,
JDual,
JDual,
JDual,
JDual,
JRight,
JRight,
JRight,
JRight,
JRight
};
typedef struct {
ArabicShape form1;
ArabicShape form2;
} JoiningPair;
static const JoiningPair joining_table[5][4] =
{
{ { XIsolated, XIsolated }, { XIsolated, XCausing }, { XIsolated, XInitial }, { XIsolated, XIsolated } },
{ { XFinal, XIsolated }, { XFinal, XCausing }, { XFinal, XInitial }, { XFinal, XIsolated } },
{ { XIsolated, XIsolated }, { XInitial, XCausing }, { XInitial, XMedial }, { XInitial, XFinal } },
{ { XFinal, XIsolated }, { XMedial, XCausing }, { XMedial, XMedial }, { XMedial, XFinal } },
{ { XIsolated, XIsolated }, { XIsolated, XCausing }, { XIsolated, XMedial }, { XIsolated, XFinal } },
};
static void getArabicProperties(const unsigned short *chars, int len, HB_ArabicProperties *properties)
{
int lastPos = 0;
int lastGroup = ArabicNone;
int i = 0;
ArabicGroup group = arabicGroup(chars[0]);
Joining j = joining_for_group[group];
ArabicShape shape = joining_table[XIsolated][j].form2;
properties[0].justification = HB_NoJustification;
for (i = 1; i < len; ++i) {
properties[i].justification = HB_NoJustification;
group = arabicGroup(chars[i]);
j = joining_for_group[group];
if (j == JTransparent) {
properties[i].shape = XIsolated;
continue;
}
properties[lastPos].shape = joining_table[shape][j].form1;
shape = joining_table[shape][j].form2;
switch(lastGroup) {
case Seen:
if (properties[lastPos].shape == XInitial || properties[lastPos].shape == XMedial)
properties[i-1].justification = HB_Arabic_Seen;
break;
case Hah:
if (properties[lastPos].shape == XFinal)
properties[lastPos-1].justification = HB_Arabic_HaaDal;
break;
case Alef:
if (properties[lastPos].shape == XFinal)
properties[lastPos-1].justification = HB_Arabic_Alef;
break;
case Ain:
if (properties[lastPos].shape == XFinal)
properties[lastPos-1].justification = HB_Arabic_Waw;
break;
case Noon:
if (properties[lastPos].shape == XFinal)
properties[lastPos-1].justification = HB_Arabic_Normal;
break;
case ArabicNone:
break;
default:
assert(FALSE);
}
lastGroup = ArabicNone;
switch(group) {
case ArabicNone:
case Transparent:
case Center:
break;
case ArabicSpace:
properties[i].justification = HB_Arabic_Space;
break;
case Kashida:
properties[i].justification = HB_Arabic_Kashida;
break;
case Seen:
lastGroup = Seen;
break;
case Hah:
case Dal:
lastGroup = Hah;
break;
case Alef:
case Tah:
lastGroup = Alef;
break;
case Yeh:
case Reh:
if (properties[lastPos].shape == XMedial && arabicGroup(chars[lastPos]) == Beh)
properties[lastPos-1].justification = HB_Arabic_BaRa;
break;
case Ain:
case Waw:
lastGroup = Ain;
break;
case Noon:
case Beh:
case HamzaOnHehGoal:
lastGroup = Noon;
break;
case ArabicGroupsEnd:
assert(FALSE);
}
lastPos = i;
}
properties[lastPos].shape = joining_table[shape][JNone].form1;
}
static Joining getNkoJoining(unsigned short uc)
{
if (uc < 0x7ca)
return JNone;
if (uc <= 0x7ea)
return JDual;
if (uc <= 0x7f3)
return JTransparent;
if (uc <= 0x7f9)
return JNone;
if (uc == 0x7fa)
return JCausing;
return JNone;
}
static void getNkoProperties(const unsigned short *chars, int len, HB_ArabicProperties *properties)
{
int lastPos = 0;
int i = 0;
Joining j = getNkoJoining(chars[0]);
ArabicShape shape = joining_table[XIsolated][j].form2;
properties[0].justification = HB_NoJustification;
for (i = 1; i < len; ++i) {
properties[i].justification = (HB_GetUnicodeCharCategory(chars[i]) == HB_Separator_Space) ?
ArabicSpace : ArabicNone;
j = getNkoJoining(chars[i]);
if (j == JTransparent) {
properties[i].shape = XIsolated;
continue;
}
properties[lastPos].shape = joining_table[shape][j].form1;
shape = joining_table[shape][j].form2;
lastPos = i;
}
properties[lastPos].shape = joining_table[shape][JNone].form1;
}
static const hb_uint16 arabicUnicodeMapping[256][2] = {
{ 0x0600, 0 },
{ 0x0601, 0 },
{ 0x0602, 0 },
{ 0x0603, 0 },
{ 0x0604, 0 },
{ 0x0605, 0 },
{ 0x0606, 0 },
{ 0x0607, 0 },
{ 0x0608, 0 },
{ 0x0609, 0 },
{ 0x060A, 0 },
{ 0x060B, 0 },
{ 0x060C, 0 },
{ 0x060D, 0 },
{ 0x060E, 0 },
{ 0x060F, 0 },
{ 0x0610, 0 },
{ 0x0611, 0 },
{ 0x0612, 0 },
{ 0x0613, 0 },
{ 0x0614, 0 },
{ 0x0615, 0 },
{ 0x0616, 0 },
{ 0x0617, 0 },
{ 0x0618, 0 },
{ 0x0619, 0 },
{ 0x061A, 0 },
{ 0x061B, 0 },
{ 0x061C, 0 },
{ 0x061D, 0 },
{ 0x061E, 0 },
{ 0x061F, 0 },
{ 0x0620, 0 },
{ 0xFE80, 0 },
{ 0xFE81, 1 },
{ 0xFE83, 1 },
{ 0xFE85, 1 },
{ 0xFE87, 1 },
{ 0xFE89, 3 },
{ 0xFE8D, 1 },
{ 0xFE8F, 3 },
{ 0xFE93, 1 },
{ 0xFE95, 3 },
{ 0xFE99, 3 },
{ 0xFE9D, 3 },
{ 0xFEA1, 3 },
{ 0xFEA5, 3 },
{ 0xFEA9, 1 },
{ 0xFEAB, 1 },
{ 0xFEAD, 1 },
{ 0xFEAF, 1 },
{ 0xFEB1, 3 },
{ 0xFEB5, 3 },
{ 0xFEB9, 3 },
{ 0xFEBD, 3 },
{ 0xFEC1, 3 },
{ 0xFEC5, 3 },
{ 0xFEC9, 3 },
{ 0xFECD, 3 },
{ 0x063B, 0 },
{ 0x063C, 0 },
{ 0x063D, 0 },
{ 0x063E, 0 },
{ 0x063F, 0 },
{ 0x0640, 0 },
{ 0xFED1, 3 },
{ 0xFED5, 3 },
{ 0xFED9, 3 },
{ 0xFEDD, 3 },
{ 0xFEE1, 3 },
{ 0xFEE5, 3 },
{ 0xFEE9, 3 },
{ 0xFEED, 1 },
{ 0x0649, 3 },
{ 0xFEF1, 3 },
{ 0x064B, 0 },
{ 0x064C, 0 },
{ 0x064D, 0 },
{ 0x064E, 0 },
{ 0x064F, 0 },
{ 0x0650, 0 },
{ 0x0651, 0 },
{ 0x0652, 0 },
{ 0x0653, 0 },
{ 0x0654, 0 },
{ 0x0655, 0 },
{ 0x0656, 0 },
{ 0x0657, 0 },
{ 0x0658, 0 },
{ 0x0659, 0 },
{ 0x065A, 0 },
{ 0x065B, 0 },
{ 0x065C, 0 },
{ 0x065D, 0 },
{ 0x065E, 0 },
{ 0x065F, 0 },
{ 0x0660, 0 },
{ 0x0661, 0 },
{ 0x0662, 0 },
{ 0x0663, 0 },
{ 0x0664, 0 },
{ 0x0665, 0 },
{ 0x0666, 0 },
{ 0x0667, 0 },
{ 0x0668, 0 },
{ 0x0669, 0 },
{ 0x066A, 0 },
{ 0x066B, 0 },
{ 0x066C, 0 },
{ 0x066D, 0 },
{ 0x066E, 0 },
{ 0x066F, 0 },
{ 0x0670, 0 },
{ 0xFB50, 1 },
{ 0x0672, 0 },
{ 0x0673, 0 },
{ 0x0674, 0 },
{ 0x0675, 0 },
{ 0x0676, 0 },
{ 0x0677, 0 },
{ 0x0678, 0 },
{ 0xFB66, 3 },
{ 0xFB5E, 3 },
{ 0xFB52, 3 },
{ 0x067C, 0 },
{ 0x067D, 0 },
{ 0xFB56, 3 },
{ 0xFB62, 3 },
{ 0xFB5A, 3 },
{ 0x0681, 0 },
{ 0x0682, 0 },
{ 0xFB76, 3 },
{ 0xFB72, 3 },
{ 0x0685, 0 },
{ 0xFB7A, 3 },
{ 0xFB7E, 3 },
{ 0xFB88, 1 },
{ 0x0689, 0 },
{ 0x068A, 0 },
{ 0x068B, 0 },
{ 0xFB84, 1 },
{ 0xFB82, 1 },
{ 0xFB86, 1 },
{ 0x068F, 0 },
{ 0x0690, 0 },
{ 0xFB8C, 1 },
{ 0x0692, 0 },
{ 0x0693, 0 },
{ 0x0694, 0 },
{ 0x0695, 0 },
{ 0x0696, 0 },
{ 0x0697, 0 },
{ 0xFB8A, 1 },
{ 0x0699, 0 },
{ 0x069A, 0 },
{ 0x069B, 0 },
{ 0x069C, 0 },
{ 0x069D, 0 },
{ 0x069E, 0 },
{ 0x069F, 0 },
{ 0x06A0, 0 },
{ 0x06A1, 0 },
{ 0x06A2, 0 },
{ 0x06A3, 0 },
{ 0xFB6A, 3 },
{ 0x06A5, 0 },
{ 0xFB6E, 3 },
{ 0x06A7, 0 },
{ 0x06A8, 0 },
{ 0xFB8E, 3 },
{ 0x06AA, 0 },
{ 0x06AB, 0 },
{ 0x06AC, 0 },
{ 0xFBD3, 3 },
{ 0x06AE, 0 },
{ 0xFB92, 3 },
{ 0x06B0, 0 },
{ 0xFB9A, 3 },
{ 0x06B2, 0 },
{ 0xFB96, 3 },
{ 0x06B4, 0 },
{ 0x06B5, 0 },
{ 0x06B6, 0 },
{ 0x06B7, 0 },
{ 0x06B8, 0 },
{ 0x06B9, 0 },
{ 0xFB9E, 1 },
{ 0xFBA0, 3 },
{ 0x06BC, 0 },
{ 0x06BD, 0 },
{ 0xFBAA, 3 },
{ 0x06BF, 0 },
{ 0xFBA4, 1 },
{ 0xFBA6, 3 },
{ 0x06C2, 0 },
{ 0x06C3, 0 },
{ 0x06C4, 0 },
{ 0xFBE0, 1 },
{ 0xFBD9, 1 },
{ 0xFBD7, 1 },
{ 0xFBDB, 1 },
{ 0xFBE2, 1 },
{ 0x06CA, 0 },
{ 0xFBDE, 1 },
{ 0xFBFC, 3 },
{ 0x06CD, 0 },
{ 0x06CE, 0 },
{ 0x06CF, 0 },
{ 0xFBE4, 3 },
{ 0x06D1, 0 },
{ 0xFBAE, 1 },
{ 0xFBB0, 1 },
{ 0x06D4, 0 },
{ 0x06D5, 0 },
{ 0x06D6, 0 },
{ 0x06D7, 0 },
{ 0x06D8, 0 },
{ 0x06D9, 0 },
{ 0x06DA, 0 },
{ 0x06DB, 0 },
{ 0x06DC, 0 },
{ 0x06DD, 0 },
{ 0x06DE, 0 },
{ 0x06DF, 0 },
{ 0x06E0, 0 },
{ 0x06E1, 0 },
{ 0x06E2, 0 },
{ 0x06E3, 0 },
{ 0x06E4, 0 },
{ 0x06E5, 0 },
{ 0x06E6, 0 },
{ 0x06E7, 0 },
{ 0x06E8, 0 },
{ 0x06E9, 0 },
{ 0x06EA, 0 },
{ 0x06EB, 0 },
{ 0x06EC, 0 },
{ 0x06ED, 0 },
{ 0x06EE, 0 },
{ 0x06EF, 0 },
{ 0x06F0, 0 },
{ 0x06F1, 0 },
{ 0x06F2, 0 },
{ 0x06F3, 0 },
{ 0x06F4, 0 },
{ 0x06F5, 0 },
{ 0x06F6, 0 },
{ 0x06F7, 0 },
{ 0x06F8, 0 },
{ 0x06F9, 0 },
{ 0x06FA, 0 },
{ 0x06FB, 0 },
{ 0x06FC, 0 },
{ 0x06FD, 0 },
{ 0x06FE, 0 },
{ 0x06FF, 0 }
};
static const hb_uint16 alefMaksura[4] = {0xFEEF, 0xFEF0, 0xFBE8, 0xFBE9};
static const hb_uint16 arabicUnicodeLamAlefMapping[6][4] = {
{ 0xfffd, 0xfffd, 0xfef5, 0xfef6 },
{ 0xfffd, 0xfffd, 0xfef7, 0xfef8 },
{ 0xfffd, 0xfffd, 0xfffd, 0xfffd },
{ 0xfffd, 0xfffd, 0xfef9, 0xfefa },
{ 0xfffd, 0xfffd, 0xfffd, 0xfffd },
{ 0xfffd, 0xfffd, 0xfefb, 0xfefc }
};
static int getShape(hb_uint8 cell, int shape)
{
int ch = (cell != 0x49)
? (shape ? arabicUnicodeMapping[cell][0] + shape : 0x600+cell)
: alefMaksura[shape] ;
return ch;
}
static HB_UChar16 prevChar(const HB_UChar16 *str, int pos)
{
const HB_UChar16 *ch = str + pos - 1;
pos--;
while(pos > -1) {
if(HB_GetUnicodeCharCategory(*ch) != HB_Mark_NonSpacing)
return *ch;
pos--;
ch--;
}
return ReplacementCharacter;
}
static HB_UChar16 nextChar(const HB_UChar16 *str, hb_uint32 len, hb_uint32 pos)
{
const HB_UChar16 *ch = str + pos + 1;
pos++;
while(pos < len) {
if(HB_GetUnicodeCharCategory(*ch) != HB_Mark_NonSpacing)
return *ch;
pos++;
ch++;
}
return ReplacementCharacter;
}
static void shapedString(const HB_UChar16 *uc, hb_uint32 stringLength, hb_uint32 from, hb_uint32 len, HB_UChar16 *shapeBuffer, int *shapedLength,
HB_Bool reverse, HB_GlyphAttributes *attributes, unsigned short *logClusters)
{
HB_ArabicProperties *properties;
hb_int32 f = from;
hb_uint32 l = len;
const HB_UChar16 *ch;
HB_UChar16 *data;
int clusterStart;
hb_uint32 i;
HB_STACKARRAY(HB_ArabicProperties, props, len + 2);
properties = props;
assert(stringLength >= from + len);
if(len == 0) {
*shapedLength = 0;
return;
}
if (from > 0) {
--f;
++l;
++properties;
}
if (f + l < stringLength)
++l;
getArabicProperties(uc+f, l, props);
ch = uc + from;
data = shapeBuffer;
clusterStart = 0;
for (i = 0; i < len; i++) {
hb_uint8 r = *ch >> 8;
int gpos = data - shapeBuffer;
if (r != 0x06) {
if (r == 0x20) {
if (*ch == 0x200c || *ch == 0x200d)
goto skip;
}
if (reverse)
*data = HB_GetMirroredChar(*ch);
else
*data = *ch;
} else {
hb_uint8 c = *ch & 0xff;
int pos = i + from;
int shape = properties[i].shape;
hb_uint16 map;
switch (c) {
case 0x44: {
const HB_UChar16 pch = nextChar(uc, stringLength, pos);
if ((pch >> 8) == 0x06) {
switch (pch & 0xff) {
case 0x22:
case 0x23:
case 0x25:
case 0x27:
map = arabicUnicodeLamAlefMapping[(pch & 0xff) - 0x22][shape];
goto next;
default:
break;
}
}
break;
}
case 0x22:
case 0x23:
case 0x25:
case 0x27:
if (prevChar(uc, pos) == 0x0644) {
goto skip;
}
default:
break;
}
map = getShape(c, shape);
next:
*data = map;
}
if (HB_GetUnicodeCharCategory(*ch) == HB_Mark_NonSpacing) {
attributes[gpos].mark = TRUE;
} else {
attributes[gpos].mark = FALSE;
clusterStart = data - shapeBuffer;
}
attributes[gpos].clusterStart = !attributes[gpos].mark;
attributes[gpos].combiningClass = HB_GetUnicodeCharCombiningClass(*ch);
attributes[gpos].justification = properties[i].justification;
data++;
skip:
ch++;
logClusters[i] = clusterStart;
}
*shapedLength = data - shapeBuffer;
HB_FREE_STACKARRAY(props);
}
#ifndef NO_OPENTYPE
static const HB_OpenTypeFeature arabic_features[] = {
{ HB_MAKE_TAG('c', 'c', 'm', 'p'), CcmpProperty },
{ HB_MAKE_TAG('i', 's', 'o', 'l'), IsolProperty },
{ HB_MAKE_TAG('f', 'i', 'n', 'a'), FinaProperty },
{ HB_MAKE_TAG('m', 'e', 'd', 'i'), MediProperty },
{ HB_MAKE_TAG('i', 'n', 'i', 't'), InitProperty },
{ HB_MAKE_TAG('r', 'l', 'i', 'g'), RligProperty },
{ HB_MAKE_TAG('c', 'a', 'l', 't'), CaltProperty },
{ HB_MAKE_TAG('l', 'i', 'g', 'a'), LigaProperty },
{ HB_MAKE_TAG('d', 'l', 'i', 'g'), DligProperty },
{ HB_MAKE_TAG('c', 's', 'w', 'h'), CswhProperty },
{ HB_MAKE_TAG('m', 's', 'e', 't'), MsetProperty },
{0, 0}
};
static const HB_OpenTypeFeature syriac_features[] = {
{ HB_MAKE_TAG('c', 'c', 'm', 'p'), CcmpProperty },
{ HB_MAKE_TAG('i', 's', 'o', 'l'), IsolProperty },
{ HB_MAKE_TAG('f', 'i', 'n', 'a'), FinaProperty },
{ HB_MAKE_TAG('f', 'i', 'n', '2'), FinaProperty },
{ HB_MAKE_TAG('f', 'i', 'n', '3'), FinaProperty },
{ HB_MAKE_TAG('m', 'e', 'd', 'i'), MediProperty },
{ HB_MAKE_TAG('m', 'e', 'd', '2'), MediProperty },
{ HB_MAKE_TAG('i', 'n', 'i', 't'), InitProperty },
{ HB_MAKE_TAG('r', 'l', 'i', 'g'), RligProperty },
{ HB_MAKE_TAG('c', 'a', 'l', 't'), CaltProperty },
{ HB_MAKE_TAG('l', 'i', 'g', 'a'), LigaProperty },
{ HB_MAKE_TAG('d', 'l', 'i', 'g'), DligProperty },
{0, 0}
};
static HB_Bool arabicSyriacOpenTypeShape(HB_ShaperItem *item, HB_Bool *ot_ok)
{
const HB_UChar16 *uc;
const int nglyphs = item->num_glyphs;
hb_int32 f;
hb_uint32 l;
HB_ArabicProperties *properties;
HB_DECLARE_STACKARRAY(HB_ArabicProperties, props)
HB_DECLARE_STACKARRAY(hb_uint32, apply)
HB_Bool shaped;
int i = 0;
*ot_ok = TRUE;
if (!HB_ConvertStringToGlyphIndices(item))
return FALSE;
HB_HeuristicSetGlyphAttributes(item);
HB_INIT_STACKARRAY(HB_ArabicProperties, props, item->item.length + 2);
HB_INIT_STACKARRAY(hb_uint32, apply, item->num_glyphs);
uc = item->string + item->item.pos;
properties = props;
f = 0;
l = item->item.length;
if (item->item.pos > 0) {
--f;
++l;
++properties;
}
if (f + l + item->item.pos < item->stringLength) {
++l;
}
if (item->item.script == HB_Script_Nko)
getNkoProperties(uc+f, l, props);
else
getArabicProperties(uc+f, l, props);
for (i = 0; i < (int)item->num_glyphs; i++) {
apply[i] = 0;
if (properties[i].shape == XIsolated)
apply[i] |= MediProperty|FinaProperty|InitProperty;
else if (properties[i].shape == XMedial)
apply[i] |= IsolProperty|FinaProperty|InitProperty;
else if (properties[i].shape == XFinal)
apply[i] |= IsolProperty|MediProperty|InitProperty;
else if (properties[i].shape == XInitial)
apply[i] |= IsolProperty|MediProperty|FinaProperty;
item->attributes[i].justification = properties[i].justification;
}
HB_FREE_STACKARRAY(props);
shaped = HB_OpenTypeShape(item, apply);
HB_FREE_STACKARRAY(apply);
if (!shaped) {
*ot_ok = FALSE;
return FALSE;
}
return HB_OpenTypePosition(item, nglyphs, TRUE);
}
#endif
HB_Bool HB_ArabicShape(HB_ShaperItem *item)
{
int slen;
HB_Bool haveGlyphs;
HB_STACKARRAY(HB_UChar16, shapedChars, item->item.length);
assert(item->item.script == HB_Script_Arabic || item->item.script == HB_Script_Syriac
|| item->item.script == HB_Script_Nko);
item->shaperFlags |= HB_ShaperFlag_ForceMarksToZeroWidth;
#ifndef NO_OPENTYPE
if (HB_SelectScript(item, item->item.script == HB_Script_Arabic ? arabic_features : syriac_features)) {
HB_Bool ot_ok;
if (arabicSyriacOpenTypeShape(item, &ot_ok))
return TRUE;
if (ot_ok)
return FALSE;
}
#endif
if (item->item.script != HB_Script_Arabic)
return HB_BasicShape(item);
shapedString(item->string, item->stringLength, item->item.pos, item->item.length, shapedChars, &slen,
item->item.bidiLevel % 2,
item->attributes, item->log_clusters);
haveGlyphs = item->font->klass
->convertStringToGlyphIndices(item->font,
shapedChars, slen,
item->glyphs, &item->num_glyphs,
item->item.bidiLevel % 2);
HB_FREE_STACKARRAY(shapedChars);
if (!haveGlyphs)
return FALSE;
HB_HeuristicPosition(item);
return TRUE;
}