Imported Upstream version 0.9.3
[platform/upstream/harfbuzz.git] / src / hb-old / harfbuzz-arabic.c
1 /*
2  * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
3  *
4  * This is part of HarfBuzz, an OpenType Layout engine library.
5  *
6  * Permission is hereby granted, without written agreement and without
7  * license or royalty fees, to use, copy, modify, and distribute this
8  * software and its documentation for any purpose, provided that the
9  * above copyright notice and the following two paragraphs appear in
10  * all copies of this software.
11  *
12  * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14  * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15  * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
16  * DAMAGE.
17  *
18  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
21  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
23  */
24
25 #include "harfbuzz-shaper.h"
26 #include "harfbuzz-shaper-private.h"
27
28 #include <assert.h>
29
30 static const HB_UChar16 ReplacementCharacter = 0xfffd;
31
32 typedef struct {
33     unsigned char shape;
34     unsigned char justification;
35 } HB_ArabicProperties;
36
37 typedef enum {
38     XIsolated,
39     XFinal,
40     XInitial,
41     XMedial,
42     /* intermediate state */
43     XCausing
44 } ArabicShape;
45
46 /*
47 // these groups correspond to the groups defined in the Unicode standard.
48 // Some of these groups are equal with regards to both joining and line breaking behaviour,
49 // and thus have the same enum value
50 //
51 // I'm not sure the mapping of syriac to arabic enums is correct with regards to justification, but as
52 // I couldn't find any better document I'll hope for the best.
53 */
54 typedef enum {
55     /* NonJoining */
56     ArabicNone,
57     ArabicSpace,
58     /* Transparent */
59     Transparent,
60     /* Causing */
61     Center,
62     Kashida,
63
64     /* Arabic */
65     /* Dual */
66     Beh,
67     Noon,
68     Meem = Noon,
69     Heh = Noon,
70     KnottedHeh = Noon,
71     HehGoal = Noon,
72     SwashKaf = Noon,
73     Yeh,
74     Hah,
75     Seen,
76     Sad = Seen,
77     Tah,
78     Kaf = Tah,
79     Gaf = Tah,
80     Lam = Tah,
81     Ain,
82     Feh = Ain,
83     Qaf = Ain,
84     /* Right */
85     Alef,
86     Waw,
87     Dal,
88     TehMarbuta = Dal,
89     Reh,
90     HamzaOnHehGoal,
91     YehWithTail = HamzaOnHehGoal,
92     YehBarre = HamzaOnHehGoal,
93
94     /* Syriac */
95     /* Dual */
96     Beth = Beh,
97     Gamal = Ain,
98     Heth = Noon,
99     Teth = Hah,
100     Yudh = Noon,
101     Kaph = Noon,
102     Lamadh = Lam,
103     Mim = Noon,
104     Nun = Noon,
105     Semakh = Noon,
106     FinalSemakh = Noon,
107     SyriacE = Ain,
108     Pe = Ain,
109     ReversedPe = Hah,
110     Qaph = Noon,
111     Shin = Noon,
112     Fe = Ain,
113
114     /* Right */
115     Alaph = Alef,
116     Dalath = Dal,
117     He = Dal,
118     SyriacWaw = Waw,
119     Zain = Alef,
120     YudhHe = Waw,
121     Sadhe = HamzaOnHehGoal,
122     Taw = Dal,
123
124     /* Compiler bug? Otherwise ArabicGroupsEnd would be equal to Dal + 1. */
125     Dummy = HamzaOnHehGoal,
126     ArabicGroupsEnd
127 } ArabicGroup;
128
129 static const unsigned char arabic_group[0x150] = {
130     ArabicNone, ArabicNone, ArabicNone, ArabicNone,
131     ArabicNone, ArabicNone, ArabicNone, ArabicNone,
132     ArabicNone, ArabicNone, ArabicNone, ArabicNone,
133     ArabicNone, ArabicNone, ArabicNone, ArabicNone,
134
135     Transparent, Transparent, Transparent, Transparent,
136     Transparent, Transparent, ArabicNone, ArabicNone,
137     ArabicNone, ArabicNone, ArabicNone, ArabicNone,
138     ArabicNone, ArabicNone, ArabicNone, ArabicNone,
139
140     ArabicNone, ArabicNone, Alef, Alef,
141     Waw, Alef, Yeh, Alef,
142     Beh, TehMarbuta, Beh, Beh,
143     Hah, Hah, Hah, Dal,
144
145     Dal, Reh, Reh, Seen,
146     Seen, Sad, Sad, Tah,
147     Tah, Ain, Ain, ArabicNone,
148     ArabicNone, ArabicNone, ArabicNone, ArabicNone,
149
150     /* 0x640 */
151     Kashida, Feh, Qaf, Kaf,
152     Lam, Meem, Noon, Heh,
153     Waw, Yeh, Yeh, Transparent,
154     Transparent, Transparent, Transparent, Transparent,
155
156     Transparent, Transparent, Transparent, Transparent,
157     Transparent, Transparent, Transparent, Transparent,
158     Transparent, ArabicNone, ArabicNone, ArabicNone,
159     ArabicNone, ArabicNone, ArabicNone, ArabicNone,
160
161     ArabicNone, ArabicNone, ArabicNone, ArabicNone,
162     ArabicNone, ArabicNone, ArabicNone, ArabicNone,
163     ArabicNone, ArabicNone, ArabicNone, ArabicNone,
164     ArabicNone, ArabicNone, Beh, Qaf,
165
166     Transparent, Alef, Alef, Alef,
167     ArabicNone, Alef, Waw, Waw,
168     Yeh, Beh, Beh, Beh,
169     Beh, Beh, Beh, Beh,
170
171     /* 0x680 */
172     Beh, Hah, Hah, Hah,
173     Hah, Hah, Hah, Hah,
174     Dal, Dal, Dal, Dal,
175     Dal, Dal, Dal, Dal,
176
177     Dal, Reh, Reh, Reh,
178     Reh, Reh, Reh, Reh,
179     Reh, Reh, Seen, Seen,
180     Seen, Sad, Sad, Tah,
181
182     Ain, Feh, Feh, Feh,
183     Feh, Feh, Feh, Qaf,
184     Qaf, Gaf, SwashKaf, Gaf,
185     Kaf, Kaf, Kaf, Gaf,
186
187     Gaf, Gaf, Gaf, Gaf,
188     Gaf, Lam, Lam, Lam,
189     Lam, Noon, Noon, Noon,
190     Noon, Noon, KnottedHeh, Hah,
191
192     /* 0x6c0 */
193     TehMarbuta, HehGoal, HamzaOnHehGoal, HamzaOnHehGoal,
194     Waw, Waw, Waw, Waw,
195     Waw, Waw, Waw, Waw,
196     Yeh, YehWithTail, Yeh, Waw,
197
198     Yeh, Yeh, YehBarre, YehBarre,
199     ArabicNone, TehMarbuta, Transparent, Transparent,
200     Transparent, Transparent, Transparent, Transparent,
201     Transparent, ArabicNone, ArabicNone, Transparent,
202
203     Transparent, Transparent, Transparent, Transparent,
204     Transparent, ArabicNone, ArabicNone, Transparent,
205     Transparent, ArabicNone, Transparent, Transparent,
206     Transparent, Transparent, Dal, Reh,
207
208     ArabicNone, ArabicNone, ArabicNone, ArabicNone,
209     ArabicNone, ArabicNone, ArabicNone, ArabicNone,
210     ArabicNone, ArabicNone, Seen, Sad,
211     Ain, ArabicNone, ArabicNone, KnottedHeh,
212
213     /* 0x700 */
214     ArabicNone, ArabicNone, ArabicNone, ArabicNone,
215     ArabicNone, ArabicNone, ArabicNone, ArabicNone,
216     ArabicNone, ArabicNone, ArabicNone, ArabicNone,
217     ArabicNone, ArabicNone, ArabicNone, ArabicNone,
218
219     Alaph, Transparent, Beth, Gamal,
220     Gamal, Dalath, Dalath, He,
221     SyriacWaw, Zain, Heth, Teth,
222     Teth, Yudh, YudhHe, Kaph,
223
224     Lamadh, Mim, Nun, Semakh,
225     FinalSemakh, SyriacE, Pe, ReversedPe,
226     Sadhe, Qaph, Dalath, Shin,
227     Taw, Beth, Gamal, Dalath,
228
229     Transparent, Transparent, Transparent, Transparent,
230     Transparent, Transparent, Transparent, Transparent,
231     Transparent, Transparent, Transparent, Transparent,
232     Transparent, Transparent, Transparent, Transparent,
233
234     Transparent, Transparent, Transparent, Transparent,
235     Transparent, Transparent, Transparent, Transparent,
236     Transparent, Transparent, Transparent, ArabicNone,
237     ArabicNone, Zain, Kaph, Fe,
238 };
239
240 static ArabicGroup arabicGroup(unsigned short uc)
241 {
242     if (uc >= 0x0600 && uc < 0x750)
243         return (ArabicGroup) arabic_group[uc-0x600];
244     else if (uc == 0x200d)
245         return Center;
246     else if (HB_GetUnicodeCharCategory(uc) == HB_Separator_Space)
247         return ArabicSpace;
248     else
249         return ArabicNone;
250 }
251
252
253 /*
254    Arabic shaping obeys a number of rules according to the joining classes (see Unicode book, section on
255    arabic).
256
257    Each unicode char has a joining class (right, dual (left&right), center (joincausing) or transparent).
258    transparent joining is not encoded in HB_UChar16::joining(), but applies to all combining marks and format marks.
259
260    Right join-causing: dual + center
261    Left join-causing: dual + right + center
262
263    Rules are as follows (for a string already in visual order, as we have it here):
264
265    R1 Transparent characters do not affect joining behaviour.
266    R2 A right joining character, that has a right join-causing char on the right will get form XRight
267    (R3 A left joining character, that has a left join-causing char on the left will get form XLeft)
268    Note: the above rule is meaningless, as there are no pure left joining characters defined in Unicode
269    R4 A dual joining character, that has a left join-causing char on the left and a right join-causing char on
270              the right will get form XMedial
271    R5  A dual joining character, that has a right join causing char on the right, and no left join causing char on the left
272          will get form XRight
273    R6 A dual joining character, that has a  left join causing char on the left, and no right join causing char on the right
274          will get form XLeft
275    R7 Otherwise the character will get form XIsolated
276
277    Additionally we have to do the minimal ligature support for lam-alef ligatures:
278
279    L1 Transparent characters do not affect ligature behaviour.
280    L2 Any sequence of Alef(XRight) + Lam(XMedial) will form the ligature Alef.Lam(XLeft)
281    L3 Any sequence of Alef(XRight) + Lam(XLeft) will form the ligature Alef.Lam(XIsolated)
282
283    The state table below handles rules R1-R7.
284 */
285
286 typedef enum {
287     JNone,
288     JCausing,
289     JDual,
290     JRight,
291     JTransparent
292 } Joining;
293
294 static const Joining joining_for_group[ArabicGroupsEnd] = {
295     /* NonJoining */
296     JNone, /* ArabicNone */
297     JNone, /* ArabicSpace */
298     /* Transparent */
299     JTransparent, /* Transparent */
300     /* Causing */
301     JCausing, /* Center */
302     JCausing, /* Kashida */
303     /* Dual */
304     JDual, /* Beh */
305     JDual, /* Noon */
306     JDual, /* Yeh */
307     JDual, /* Hah */
308     JDual, /* Seen */
309     JDual, /* Tah */
310     JDual, /* Ain */
311     /* Right */
312     JRight, /* Alef */
313     JRight, /* Waw */
314     JRight, /* Dal */
315     JRight, /* Reh */
316     JRight  /* HamzaOnHehGoal */
317 };
318
319
320 typedef struct {
321     ArabicShape form1;
322     ArabicShape form2;
323 } JoiningPair;
324
325 static const JoiningPair joining_table[5][4] =
326 /* None, Causing, Dual, Right */
327 {
328     { { XIsolated, XIsolated }, { XIsolated, XCausing }, { XIsolated, XInitial }, { XIsolated, XIsolated } }, /* XIsolated */
329     { { XFinal, XIsolated }, { XFinal, XCausing }, { XFinal, XInitial }, { XFinal, XIsolated } }, /* XFinal */
330     { { XIsolated, XIsolated }, { XInitial, XCausing }, { XInitial, XMedial }, { XInitial, XFinal } }, /* XInitial */
331     { { XFinal, XIsolated }, { XMedial, XCausing }, { XMedial, XMedial }, { XMedial, XFinal } }, /* XMedial */
332     { { XIsolated, XIsolated }, { XIsolated, XCausing }, { XIsolated, XMedial }, { XIsolated, XFinal } }, /* XCausing */
333 };
334
335
336 /*
337 According to http://www.microsoft.com/middleeast/Arabicdev/IE6/KBase.asp
338
339 1. Find the priority of the connecting opportunities in each word
340 2. Add expansion at the highest priority connection opportunity
341 3. If more than one connection opportunity have the same highest value,
342    use the opportunity closest to the end of the word.
343
344 Following is a chart that provides the priority for connection
345 opportunities and where expansion occurs. The character group names
346 are those in table 6.6 of the UNICODE 2.0 book.
347
348
349 PrioritY        Glyph                   Condition                                       Kashida Location
350
351 Arabic_Kashida        User inserted Kashida   The user entered a Kashida in a position.       After the user
352                 (Shift+j or Shift+[E with hat])    Thus, it is the highest priority to insert an   inserted kashida
353                                         automatic kashida.
354
355 Arabic_Seen        Seen, Sad               Connecting to the next character.               After the character.
356                                         (Initial or medial form).
357
358 Arabic_HaaDal        Teh Marbutah, Haa, Dal  Connecting to previous character.               Before the final form
359                                                                                         of these characters.
360
361 Arabic_Alef     Alef, Tah, Lam,         Connecting to previous character.               Before the final form
362                 Kaf and Gaf                                                             of these characters.
363
364 Arabic_BaRa     Reh, Yeh                Connected to medial Beh                         Before preceding medial Baa
365
366 Arabic_Waw        Waw, Ain, Qaf, Feh      Connecting to previous character.               Before the final form of
367                                                                                         these characters.
368
369 Arabic_Normal   Other connecting        Connecting to previous character.               Before the final form
370                 characters                                                              of these characters.
371
372
373
374 This seems to imply that we have at most one kashida point per arabic word.
375
376 */
377
378 static void getArabicProperties(const unsigned short *chars, int len, HB_ArabicProperties *properties)
379 {
380 /*     qDebug("arabicSyriacOpenTypeShape: properties:"); */
381     int lastPos = 0;
382     int lastGroup = ArabicNone;
383     int i = 0;
384
385     ArabicGroup group = arabicGroup(chars[0]);
386     Joining j = joining_for_group[group];
387     ArabicShape shape = joining_table[XIsolated][j].form2;
388     properties[0].justification = HB_NoJustification;
389
390     for (i = 1; i < len; ++i) {
391         /* #### fix handling for spaces and punktuation */
392         properties[i].justification = HB_NoJustification;
393
394         group = arabicGroup(chars[i]);
395         j = joining_for_group[group];
396
397         if (j == JTransparent) {
398             properties[i].shape = XIsolated;
399             continue;
400         }
401
402         properties[lastPos].shape = joining_table[shape][j].form1;
403         shape = joining_table[shape][j].form2;
404
405         switch(lastGroup) {
406         case Seen:
407             if (properties[lastPos].shape == XInitial || properties[lastPos].shape == XMedial)
408                 properties[i-1].justification = HB_Arabic_Seen;
409             break;
410         case Hah:
411             if (properties[lastPos].shape == XFinal)
412                 properties[lastPos-1].justification = HB_Arabic_HaaDal;
413             break;
414         case Alef:
415             if (properties[lastPos].shape == XFinal)
416                 properties[lastPos-1].justification = HB_Arabic_Alef;
417             break;
418         case Ain:
419             if (properties[lastPos].shape == XFinal)
420                 properties[lastPos-1].justification = HB_Arabic_Waw;
421             break;
422         case Noon:
423             if (properties[lastPos].shape == XFinal)
424                 properties[lastPos-1].justification = HB_Arabic_Normal;
425             break;
426         case ArabicNone:
427             break;
428
429         default:
430             assert(FALSE);
431         }
432
433         lastGroup = ArabicNone;
434
435         switch(group) {
436         case ArabicNone:
437         case Transparent:
438         /* ### Center should probably be treated as transparent when it comes to justification. */
439         case Center:
440             break;
441         case ArabicSpace:
442             properties[i].justification = HB_Arabic_Space;
443             break;
444         case Kashida:
445             properties[i].justification = HB_Arabic_Kashida;
446             break;
447         case Seen:
448             lastGroup = Seen;
449             break;
450
451         case Hah:
452         case Dal:
453             lastGroup = Hah;
454             break;
455
456         case Alef:
457         case Tah:
458             lastGroup = Alef;
459             break;
460
461         case Yeh:
462         case Reh:
463             if (properties[lastPos].shape == XMedial && arabicGroup(chars[lastPos]) == Beh)
464                 properties[lastPos-1].justification = HB_Arabic_BaRa;
465             break;
466
467         case Ain:
468         case Waw:
469             lastGroup = Ain;
470             break;
471
472         case Noon:
473         case Beh:
474         case HamzaOnHehGoal:
475             lastGroup = Noon;
476             break;
477         case ArabicGroupsEnd:
478             assert(FALSE);
479         }
480
481         lastPos = i;
482     }
483     properties[lastPos].shape = joining_table[shape][JNone].form1;
484
485
486     /*
487      for (int i = 0; i < len; ++i)
488          qDebug("arabic properties(%d): uc=%x shape=%d, justification=%d", i, chars[i], properties[i].shape, properties[i].justification);
489     */
490 }
491
492 static Joining getNkoJoining(unsigned short uc)
493 {
494     if (uc < 0x7ca)
495         return JNone;
496     if (uc <= 0x7ea)
497         return JDual;
498     if (uc <= 0x7f3)
499         return JTransparent;
500     if (uc <= 0x7f9)
501         return JNone;
502     if (uc == 0x7fa)
503         return JCausing;
504     return JNone;
505 }
506
507 static void getNkoProperties(const unsigned short *chars, int len, HB_ArabicProperties *properties)
508 {
509     int lastPos = 0;
510     int i = 0;
511
512     Joining j = getNkoJoining(chars[0]);
513     ArabicShape shape = joining_table[XIsolated][j].form2;
514     properties[0].justification = HB_NoJustification;
515
516     for (i = 1; i < len; ++i) {
517         properties[i].justification = (HB_GetUnicodeCharCategory(chars[i]) == HB_Separator_Space) ?
518                                       ArabicSpace : ArabicNone;
519
520         j = getNkoJoining(chars[i]);
521
522         if (j == JTransparent) {
523             properties[i].shape = XIsolated;
524             continue;
525         }
526
527         properties[lastPos].shape = joining_table[shape][j].form1;
528         shape = joining_table[shape][j].form2;
529
530
531         lastPos = i;
532     }
533     properties[lastPos].shape = joining_table[shape][JNone].form1;
534
535
536     /*
537      for (int i = 0; i < len; ++i)
538          qDebug("nko properties(%d): uc=%x shape=%d, justification=%d", i, chars[i], properties[i].shape, properties[i].justification);
539     */
540 }
541
542 /*
543 // The unicode to unicode shaping codec.
544 // does only presentation forms B at the moment, but that should be enough for
545 // simple display
546 */
547 static const hb_uint16 arabicUnicodeMapping[256][2] = {
548     /* base of shaped forms, and number-1 of them (0 for non shaping,
549        1 for right binding and 3 for dual binding */
550
551     /* These are just the glyphs available in Unicode,
552        some characters are in R class, but have no glyphs in Unicode. */
553
554     { 0x0600, 0 }, /* 0x0600 */
555     { 0x0601, 0 }, /* 0x0601 */
556     { 0x0602, 0 }, /* 0x0602 */
557     { 0x0603, 0 }, /* 0x0603 */
558     { 0x0604, 0 }, /* 0x0604 */
559     { 0x0605, 0 }, /* 0x0605 */
560     { 0x0606, 0 }, /* 0x0606 */
561     { 0x0607, 0 }, /* 0x0607 */
562     { 0x0608, 0 }, /* 0x0608 */
563     { 0x0609, 0 }, /* 0x0609 */
564     { 0x060A, 0 }, /* 0x060A */
565     { 0x060B, 0 }, /* 0x060B */
566     { 0x060C, 0 }, /* 0x060C */
567     { 0x060D, 0 }, /* 0x060D */
568     { 0x060E, 0 }, /* 0x060E */
569     { 0x060F, 0 }, /* 0x060F */
570
571     { 0x0610, 0 }, /* 0x0610 */
572     { 0x0611, 0 }, /* 0x0611 */
573     { 0x0612, 0 }, /* 0x0612 */
574     { 0x0613, 0 }, /* 0x0613 */
575     { 0x0614, 0 }, /* 0x0614 */
576     { 0x0615, 0 }, /* 0x0615 */
577     { 0x0616, 0 }, /* 0x0616 */
578     { 0x0617, 0 }, /* 0x0617 */
579     { 0x0618, 0 }, /* 0x0618 */
580     { 0x0619, 0 }, /* 0x0619 */
581     { 0x061A, 0 }, /* 0x061A */
582     { 0x061B, 0 }, /* 0x061B */
583     { 0x061C, 0 }, /* 0x061C */
584     { 0x061D, 0 }, /* 0x061D */
585     { 0x061E, 0 }, /* 0x061E */
586     { 0x061F, 0 }, /* 0x061F */
587
588     { 0x0620, 0 }, /* 0x0620 */
589     { 0xFE80, 0 }, /* 0x0621            HAMZA */
590     { 0xFE81, 1 }, /* 0x0622    R       ALEF WITH MADDA ABOVE */
591     { 0xFE83, 1 }, /* 0x0623    R       ALEF WITH HAMZA ABOVE */
592     { 0xFE85, 1 }, /* 0x0624    R       WAW WITH HAMZA ABOVE */
593     { 0xFE87, 1 }, /* 0x0625    R       ALEF WITH HAMZA BELOW */
594     { 0xFE89, 3 }, /* 0x0626    D       YEH WITH HAMZA ABOVE */
595     { 0xFE8D, 1 }, /* 0x0627    R       ALEF */
596     { 0xFE8F, 3 }, /* 0x0628    D       BEH */
597     { 0xFE93, 1 }, /* 0x0629    R       TEH MARBUTA */
598     { 0xFE95, 3 }, /* 0x062A    D       TEH */
599     { 0xFE99, 3 }, /* 0x062B    D       THEH */
600     { 0xFE9D, 3 }, /* 0x062C    D       JEEM */
601     { 0xFEA1, 3 }, /* 0x062D    D       HAH */
602     { 0xFEA5, 3 }, /* 0x062E    D       KHAH */
603     { 0xFEA9, 1 }, /* 0x062F    R       DAL */
604
605     { 0xFEAB, 1 }, /* 0x0630    R       THAL */
606     { 0xFEAD, 1 }, /* 0x0631    R       REH */
607     { 0xFEAF, 1 }, /* 0x0632    R       ZAIN */
608     { 0xFEB1, 3 }, /* 0x0633    D       SEEN */
609     { 0xFEB5, 3 }, /* 0x0634    D       SHEEN */
610     { 0xFEB9, 3 }, /* 0x0635    D       SAD */
611     { 0xFEBD, 3 }, /* 0x0636    D       DAD */
612     { 0xFEC1, 3 }, /* 0x0637    D       TAH */
613     { 0xFEC5, 3 }, /* 0x0638    D       ZAH */
614     { 0xFEC9, 3 }, /* 0x0639    D       AIN */
615     { 0xFECD, 3 }, /* 0x063A    D       GHAIN */
616     { 0x063B, 0 }, /* 0x063B */
617     { 0x063C, 0 }, /* 0x063C */
618     { 0x063D, 0 }, /* 0x063D */
619     { 0x063E, 0 }, /* 0x063E */
620     { 0x063F, 0 }, /* 0x063F */
621
622     { 0x0640, 0 }, /* 0x0640    C       TATWEEL // ### Join Causing, only one glyph */
623     { 0xFED1, 3 }, /* 0x0641    D       FEH */
624     { 0xFED5, 3 }, /* 0x0642    D       QAF */
625     { 0xFED9, 3 }, /* 0x0643    D       KAF */
626     { 0xFEDD, 3 }, /* 0x0644    D       LAM */
627     { 0xFEE1, 3 }, /* 0x0645    D       MEEM */
628     { 0xFEE5, 3 }, /* 0x0646    D       NOON */
629     { 0xFEE9, 3 }, /* 0x0647    D       HEH */
630     { 0xFEED, 1 }, /* 0x0648    R       WAW */
631     { 0x0649, 3 }, /* 0x0649            ALEF MAKSURA // ### Dual, glyphs not consecutive, handle in code. */
632     { 0xFEF1, 3 }, /* 0x064A    D       YEH */
633     { 0x064B, 0 }, /* 0x064B */
634     { 0x064C, 0 }, /* 0x064C */
635     { 0x064D, 0 }, /* 0x064D */
636     { 0x064E, 0 }, /* 0x064E */
637     { 0x064F, 0 }, /* 0x064F */
638
639     { 0x0650, 0 }, /* 0x0650 */
640     { 0x0651, 0 }, /* 0x0651 */
641     { 0x0652, 0 }, /* 0x0652 */
642     { 0x0653, 0 }, /* 0x0653 */
643     { 0x0654, 0 }, /* 0x0654 */
644     { 0x0655, 0 }, /* 0x0655 */
645     { 0x0656, 0 }, /* 0x0656 */
646     { 0x0657, 0 }, /* 0x0657 */
647     { 0x0658, 0 }, /* 0x0658 */
648     { 0x0659, 0 }, /* 0x0659 */
649     { 0x065A, 0 }, /* 0x065A */
650     { 0x065B, 0 }, /* 0x065B */
651     { 0x065C, 0 }, /* 0x065C */
652     { 0x065D, 0 }, /* 0x065D */
653     { 0x065E, 0 }, /* 0x065E */
654     { 0x065F, 0 }, /* 0x065F */
655
656     { 0x0660, 0 }, /* 0x0660 */
657     { 0x0661, 0 }, /* 0x0661 */
658     { 0x0662, 0 }, /* 0x0662 */
659     { 0x0663, 0 }, /* 0x0663 */
660     { 0x0664, 0 }, /* 0x0664 */
661     { 0x0665, 0 }, /* 0x0665 */
662     { 0x0666, 0 }, /* 0x0666 */
663     { 0x0667, 0 }, /* 0x0667 */
664     { 0x0668, 0 }, /* 0x0668 */
665     { 0x0669, 0 }, /* 0x0669 */
666     { 0x066A, 0 }, /* 0x066A */
667     { 0x066B, 0 }, /* 0x066B */
668     { 0x066C, 0 }, /* 0x066C */
669     { 0x066D, 0 }, /* 0x066D */
670     { 0x066E, 0 }, /* 0x066E */
671     { 0x066F, 0 }, /* 0x066F */
672
673     { 0x0670, 0 }, /* 0x0670 */
674     { 0xFB50, 1 }, /* 0x0671    R       ALEF WASLA */
675     { 0x0672, 0 }, /* 0x0672 */
676     { 0x0673, 0 }, /* 0x0673 */
677     { 0x0674, 0 }, /* 0x0674 */
678     { 0x0675, 0 }, /* 0x0675 */
679     { 0x0676, 0 }, /* 0x0676 */
680     { 0x0677, 0 }, /* 0x0677 */
681     { 0x0678, 0 }, /* 0x0678 */
682     { 0xFB66, 3 }, /* 0x0679    D       TTEH */
683     { 0xFB5E, 3 }, /* 0x067A    D       TTEHEH */
684     { 0xFB52, 3 }, /* 0x067B    D       BEEH */
685     { 0x067C, 0 }, /* 0x067C */
686     { 0x067D, 0 }, /* 0x067D */
687     { 0xFB56, 3 }, /* 0x067E    D       PEH */
688     { 0xFB62, 3 }, /* 0x067F    D       TEHEH */
689
690     { 0xFB5A, 3 }, /* 0x0680    D       BEHEH */
691     { 0x0681, 0 }, /* 0x0681 */
692     { 0x0682, 0 }, /* 0x0682 */
693     { 0xFB76, 3 }, /* 0x0683    D       NYEH */
694     { 0xFB72, 3 }, /* 0x0684    D       DYEH */
695     { 0x0685, 0 }, /* 0x0685 */
696     { 0xFB7A, 3 }, /* 0x0686    D       TCHEH */
697     { 0xFB7E, 3 }, /* 0x0687    D       TCHEHEH */
698     { 0xFB88, 1 }, /* 0x0688    R       DDAL */
699     { 0x0689, 0 }, /* 0x0689 */
700     { 0x068A, 0 }, /* 0x068A */
701     { 0x068B, 0 }, /* 0x068B */
702     { 0xFB84, 1 }, /* 0x068C    R       DAHAL */
703     { 0xFB82, 1 }, /* 0x068D    R       DDAHAL */
704     { 0xFB86, 1 }, /* 0x068E    R       DUL */
705     { 0x068F, 0 }, /* 0x068F */
706
707     { 0x0690, 0 }, /* 0x0690 */
708     { 0xFB8C, 1 }, /* 0x0691    R       RREH */
709     { 0x0692, 0 }, /* 0x0692 */
710     { 0x0693, 0 }, /* 0x0693 */
711     { 0x0694, 0 }, /* 0x0694 */
712     { 0x0695, 0 }, /* 0x0695 */
713     { 0x0696, 0 }, /* 0x0696 */
714     { 0x0697, 0 }, /* 0x0697 */
715     { 0xFB8A, 1 }, /* 0x0698    R       JEH */
716     { 0x0699, 0 }, /* 0x0699 */
717     { 0x069A, 0 }, /* 0x069A */
718     { 0x069B, 0 }, /* 0x069B */
719     { 0x069C, 0 }, /* 0x069C */
720     { 0x069D, 0 }, /* 0x069D */
721     { 0x069E, 0 }, /* 0x069E */
722     { 0x069F, 0 }, /* 0x069F */
723
724     { 0x06A0, 0 }, /* 0x06A0 */
725     { 0x06A1, 0 }, /* 0x06A1 */
726     { 0x06A2, 0 }, /* 0x06A2 */
727     { 0x06A3, 0 }, /* 0x06A3 */
728     { 0xFB6A, 3 }, /* 0x06A4    D       VEH */
729     { 0x06A5, 0 }, /* 0x06A5 */
730     { 0xFB6E, 3 }, /* 0x06A6    D       PEHEH */
731     { 0x06A7, 0 }, /* 0x06A7 */
732     { 0x06A8, 0 }, /* 0x06A8 */
733     { 0xFB8E, 3 }, /* 0x06A9    D       KEHEH */
734     { 0x06AA, 0 }, /* 0x06AA */
735     { 0x06AB, 0 }, /* 0x06AB */
736     { 0x06AC, 0 }, /* 0x06AC */
737     { 0xFBD3, 3 }, /* 0x06AD    D       NG */
738     { 0x06AE, 0 }, /* 0x06AE */
739     { 0xFB92, 3 }, /* 0x06AF    D       GAF */
740
741     { 0x06B0, 0 }, /* 0x06B0 */
742     { 0xFB9A, 3 }, /* 0x06B1    D       NGOEH */
743     { 0x06B2, 0 }, /* 0x06B2 */
744     { 0xFB96, 3 }, /* 0x06B3    D       GUEH */
745     { 0x06B4, 0 }, /* 0x06B4 */
746     { 0x06B5, 0 }, /* 0x06B5 */
747     { 0x06B6, 0 }, /* 0x06B6 */
748     { 0x06B7, 0 }, /* 0x06B7 */
749     { 0x06B8, 0 }, /* 0x06B8 */
750     { 0x06B9, 0 }, /* 0x06B9 */
751     { 0xFB9E, 1 }, /* 0x06BA    R       NOON GHUNNA */
752     { 0xFBA0, 3 }, /* 0x06BB    D       RNOON */
753     { 0x06BC, 0 }, /* 0x06BC */
754     { 0x06BD, 0 }, /* 0x06BD */
755     { 0xFBAA, 3 }, /* 0x06BE    D       HEH DOACHASHMEE */
756     { 0x06BF, 0 }, /* 0x06BF */
757
758     { 0xFBA4, 1 }, /* 0x06C0    R       HEH WITH YEH ABOVE */
759     { 0xFBA6, 3 }, /* 0x06C1    D       HEH GOAL */
760     { 0x06C2, 0 }, /* 0x06C2 */
761     { 0x06C3, 0 }, /* 0x06C3 */
762     { 0x06C4, 0 }, /* 0x06C4 */
763     { 0xFBE0, 1 }, /* 0x06C5    R       KIRGHIZ OE */
764     { 0xFBD9, 1 }, /* 0x06C6    R       OE */
765     { 0xFBD7, 1 }, /* 0x06C7    R       U */
766     { 0xFBDB, 1 }, /* 0x06C8    R       YU */
767     { 0xFBE2, 1 }, /* 0x06C9    R       KIRGHIZ YU */
768     { 0x06CA, 0 }, /* 0x06CA */
769     { 0xFBDE, 1 }, /* 0x06CB    R       VE */
770     { 0xFBFC, 3 }, /* 0x06CC    D       FARSI YEH */
771     { 0x06CD, 0 }, /* 0x06CD */
772     { 0x06CE, 0 }, /* 0x06CE */
773     { 0x06CF, 0 }, /* 0x06CF */
774
775     { 0xFBE4, 3 }, /* 0x06D0    D       E */
776     { 0x06D1, 0 }, /* 0x06D1 */
777     { 0xFBAE, 1 }, /* 0x06D2    R       YEH BARREE */
778     { 0xFBB0, 1 }, /* 0x06D3    R       YEH BARREE WITH HAMZA ABOVE */
779     { 0x06D4, 0 }, /* 0x06D4 */
780     { 0x06D5, 0 }, /* 0x06D5 */
781     { 0x06D6, 0 }, /* 0x06D6 */
782     { 0x06D7, 0 }, /* 0x06D7 */
783     { 0x06D8, 0 }, /* 0x06D8 */
784     { 0x06D9, 0 }, /* 0x06D9 */
785     { 0x06DA, 0 }, /* 0x06DA */
786     { 0x06DB, 0 }, /* 0x06DB */
787     { 0x06DC, 0 }, /* 0x06DC */
788     { 0x06DD, 0 }, /* 0x06DD */
789     { 0x06DE, 0 }, /* 0x06DE */
790     { 0x06DF, 0 }, /* 0x06DF */
791
792     { 0x06E0, 0 }, /* 0x06E0 */
793     { 0x06E1, 0 }, /* 0x06E1 */
794     { 0x06E2, 0 }, /* 0x06E2 */
795     { 0x06E3, 0 }, /* 0x06E3 */
796     { 0x06E4, 0 }, /* 0x06E4 */
797     { 0x06E5, 0 }, /* 0x06E5 */
798     { 0x06E6, 0 }, /* 0x06E6 */
799     { 0x06E7, 0 }, /* 0x06E7 */
800     { 0x06E8, 0 }, /* 0x06E8 */
801     { 0x06E9, 0 }, /* 0x06E9 */
802     { 0x06EA, 0 }, /* 0x06EA */
803     { 0x06EB, 0 }, /* 0x06EB */
804     { 0x06EC, 0 }, /* 0x06EC */
805     { 0x06ED, 0 }, /* 0x06ED */
806     { 0x06EE, 0 }, /* 0x06EE */
807     { 0x06EF, 0 }, /* 0x06EF */
808
809     { 0x06F0, 0 }, /* 0x06F0 */
810     { 0x06F1, 0 }, /* 0x06F1 */
811     { 0x06F2, 0 }, /* 0x06F2 */
812     { 0x06F3, 0 }, /* 0x06F3 */
813     { 0x06F4, 0 }, /* 0x06F4 */
814     { 0x06F5, 0 }, /* 0x06F5 */
815     { 0x06F6, 0 }, /* 0x06F6 */
816     { 0x06F7, 0 }, /* 0x06F7 */
817     { 0x06F8, 0 }, /* 0x06F8 */
818     { 0x06F9, 0 }, /* 0x06F9 */
819     { 0x06FA, 0 }, /* 0x06FA */
820     { 0x06FB, 0 }, /* 0x06FB */
821     { 0x06FC, 0 }, /* 0x06FC */
822     { 0x06FD, 0 }, /* 0x06FD */
823     { 0x06FE, 0 }, /* 0x06FE */
824     { 0x06FF, 0 }  /* 0x06FF */
825 };
826
827 /* the arabicUnicodeMapping does not work for U+0649 ALEF MAKSURA, this table does */
828 static const hb_uint16 alefMaksura[4] = {0xFEEF, 0xFEF0, 0xFBE8, 0xFBE9};
829
830 /*
831 // this is a bit tricky. Alef always binds to the right, so the second parameter descibing the shape
832 // of the lam can be either initial of medial. So initial maps to the isolated form of the ligature,
833 // medial to the final form
834 */
835 static const hb_uint16 arabicUnicodeLamAlefMapping[6][4] = {
836     { 0xfffd, 0xfffd, 0xfef5, 0xfef6 }, /* 0x622        R       Alef with Madda above */
837     { 0xfffd, 0xfffd, 0xfef7, 0xfef8 }, /* 0x623        R       Alef with Hamza above */
838     { 0xfffd, 0xfffd, 0xfffd, 0xfffd }, /* 0x624        // Just to fill the table ;-) */
839     { 0xfffd, 0xfffd, 0xfef9, 0xfefa }, /* 0x625        R       Alef with Hamza below */
840     { 0xfffd, 0xfffd, 0xfffd, 0xfffd }, /* 0x626        // Just to fill the table ;-) */
841     { 0xfffd, 0xfffd, 0xfefb, 0xfefc }  /* 0x627        R       Alef */
842 };
843
844 static int getShape(hb_uint8 cell, int shape)
845 {
846     /* the arabicUnicodeMapping does not work for U+0649 ALEF MAKSURA, handle this here */
847     int ch = (cell != 0x49)
848               ? (shape ? arabicUnicodeMapping[cell][0] + shape : 0x600+cell)
849               : alefMaksura[shape] ;
850     return ch;
851 }
852
853
854 /*
855   Two small helper functions for arabic shaping.
856 */
857 static HB_UChar16 prevChar(const HB_UChar16 *str, int pos)
858 {
859     /*qDebug("leftChar: pos=%d", pos); */
860     const HB_UChar16 *ch = str + pos - 1;
861     pos--;
862     while(pos > -1) {
863         if(HB_GetUnicodeCharCategory(*ch) != HB_Mark_NonSpacing)
864             return *ch;
865         pos--;
866         ch--;
867     }
868     return ReplacementCharacter;
869 }
870
871 static HB_UChar16 nextChar(const HB_UChar16 *str, hb_uint32 len, hb_uint32 pos)
872 {
873     const HB_UChar16 *ch = str + pos + 1;
874     pos++;
875     while(pos < len) {
876         /*qDebug("rightChar: %d isLetter=%d, joining=%d", pos, ch.isLetter(), ch.joining()); */
877         if(HB_GetUnicodeCharCategory(*ch) != HB_Mark_NonSpacing)
878             return *ch;
879         /* assume it's a transparent char, this might not be 100% correct */
880         pos++;
881         ch++;
882     }
883     return ReplacementCharacter;
884 }
885
886 static void shapedString(const HB_UChar16 *uc, hb_uint32 stringLength, hb_uint32 from, hb_uint32 len, HB_UChar16 *shapeBuffer, int *shapedLength,
887                          HB_Bool reverse, HB_GlyphAttributes *attributes, unsigned short *logClusters)
888 {
889     HB_ArabicProperties *properties;
890     hb_int32 f = from;
891     hb_uint32 l = len;
892     const HB_UChar16 *ch;
893     HB_UChar16 *data;
894     int clusterStart;
895     hb_uint32 i;
896     HB_STACKARRAY(HB_ArabicProperties, props, len + 2);
897     properties = props;
898
899     assert(stringLength >= from + len);
900
901     if(len == 0) {
902         *shapedLength = 0;
903         return;
904     }
905
906     if (from > 0) {
907         --f;
908         ++l;
909         ++properties;
910     }
911     if (f + l < stringLength)
912         ++l;
913     getArabicProperties(uc+f, l, props);
914
915     ch = uc + from;
916     data = shapeBuffer;
917     clusterStart = 0;
918
919     for (i = 0; i < len; i++) {
920         hb_uint8 r = *ch >> 8;
921         int gpos = data - shapeBuffer;
922
923         if (r != 0x06) {
924             if (r == 0x20) {
925                 if (*ch == 0x200c || *ch == 0x200d)
926                     /* remove ZWJ and ZWNJ */
927                     goto skip;
928             }
929             if (reverse)
930                 *data = HB_GetMirroredChar(*ch);
931             else
932                 *data = *ch;
933         } else {
934             hb_uint8 c = *ch & 0xff;
935             int pos = i + from;
936             int shape = properties[i].shape;
937 /*            qDebug("mapping U+%x to shape %d glyph=0x%x", ch->unicode(), shape, getShape(c, shape)); */
938             /* take care of lam-alef ligatures (lam right of alef) */
939             hb_uint16 map;
940             switch (c) {
941                 case 0x44: { /* lam */
942                     const HB_UChar16 pch = nextChar(uc, stringLength, pos);
943                     if ((pch >> 8) == 0x06) {
944                         switch (pch & 0xff) {
945                             case 0x22:
946                             case 0x23:
947                             case 0x25:
948                             case 0x27:
949 /*                                 qDebug(" lam of lam-alef ligature"); */
950                                 map = arabicUnicodeLamAlefMapping[(pch & 0xff) - 0x22][shape];
951                                 goto next;
952                             default:
953                                 break;
954                         }
955                     }
956                     break;
957                 }
958                 case 0x22: /* alef with madda */
959                 case 0x23: /* alef with hamza above */
960                 case 0x25: /* alef with hamza below */
961                 case 0x27: /* alef */
962                     if (prevChar(uc, pos) == 0x0644) {
963                         /* have a lam alef ligature */
964                         /*qDebug(" alef of lam-alef ligature"); */
965                         goto skip;
966                     }
967                 default:
968                     break;
969             }
970             map = getShape(c, shape);
971         next:
972             *data = map;
973         }
974         /* ##### Fixme */
975         /*glyphs[gpos].attributes.zeroWidth = zeroWidth; */
976         if (HB_GetUnicodeCharCategory(*ch) == HB_Mark_NonSpacing) {
977             attributes[gpos].mark = TRUE;
978 /*             qDebug("glyph %d (char %d) is mark!", gpos, i); */
979         } else {
980             attributes[gpos].mark = FALSE;
981             clusterStart = data - shapeBuffer;
982         }
983         attributes[gpos].clusterStart = !attributes[gpos].mark;
984         attributes[gpos].combiningClass = HB_GetUnicodeCharCombiningClass(*ch);
985         attributes[gpos].justification = properties[i].justification;
986 /*         qDebug("data[%d] = %x (from %x)", gpos, (uint)data->unicode(), ch->unicode());*/
987         data++;
988     skip:
989         ch++;
990         logClusters[i] = clusterStart;
991     }
992     *shapedLength = data - shapeBuffer;
993
994     HB_FREE_STACKARRAY(props);
995 }
996
997 #ifndef NO_OPENTYPE
998
999 static const HB_OpenTypeFeature arabic_features[] = {
1000     { HB_MAKE_TAG('c', 'c', 'm', 'p'), CcmpProperty },
1001     { HB_MAKE_TAG('i', 's', 'o', 'l'), IsolProperty },
1002     { HB_MAKE_TAG('f', 'i', 'n', 'a'), FinaProperty },
1003     { HB_MAKE_TAG('m', 'e', 'd', 'i'), MediProperty },
1004     { HB_MAKE_TAG('i', 'n', 'i', 't'), InitProperty },
1005     { HB_MAKE_TAG('r', 'l', 'i', 'g'), RligProperty },
1006     { HB_MAKE_TAG('c', 'a', 'l', 't'), CaltProperty },
1007     { HB_MAKE_TAG('l', 'i', 'g', 'a'), LigaProperty },
1008     { HB_MAKE_TAG('d', 'l', 'i', 'g'), DligProperty },
1009     { HB_MAKE_TAG('c', 's', 'w', 'h'), CswhProperty },
1010     /* mset is used in old Win95 fonts that don't have a 'mark' positioning table. */
1011     { HB_MAKE_TAG('m', 's', 'e', 't'), MsetProperty },
1012     {0, 0}
1013 };
1014
1015 static const HB_OpenTypeFeature syriac_features[] = {
1016     { HB_MAKE_TAG('c', 'c', 'm', 'p'), CcmpProperty },
1017     { HB_MAKE_TAG('i', 's', 'o', 'l'), IsolProperty },
1018     { HB_MAKE_TAG('f', 'i', 'n', 'a'), FinaProperty },
1019     { HB_MAKE_TAG('f', 'i', 'n', '2'), FinaProperty },
1020     { HB_MAKE_TAG('f', 'i', 'n', '3'), FinaProperty },
1021     { HB_MAKE_TAG('m', 'e', 'd', 'i'), MediProperty },
1022     { HB_MAKE_TAG('m', 'e', 'd', '2'), MediProperty },
1023     { HB_MAKE_TAG('i', 'n', 'i', 't'), InitProperty },
1024     { HB_MAKE_TAG('r', 'l', 'i', 'g'), RligProperty },
1025     { HB_MAKE_TAG('c', 'a', 'l', 't'), CaltProperty },
1026     { HB_MAKE_TAG('l', 'i', 'g', 'a'), LigaProperty },
1027     { HB_MAKE_TAG('d', 'l', 'i', 'g'), DligProperty },
1028     {0, 0}
1029 };
1030
1031 static HB_Bool arabicSyriacOpenTypeShape(HB_ShaperItem *item, HB_Bool *ot_ok)
1032 {
1033     const HB_UChar16 *uc;
1034     const int nglyphs = item->num_glyphs;
1035     hb_int32 f;
1036     hb_uint32 l;
1037     HB_ArabicProperties *properties;
1038     HB_DECLARE_STACKARRAY(HB_ArabicProperties, props)
1039     HB_DECLARE_STACKARRAY(hb_uint32, apply)
1040     HB_Bool shaped;
1041     int i = 0;
1042
1043     *ot_ok = TRUE;
1044
1045     if (!HB_ConvertStringToGlyphIndices(item))
1046         return FALSE;
1047     HB_HeuristicSetGlyphAttributes(item);
1048
1049     HB_INIT_STACKARRAY(HB_ArabicProperties, props, item->item.length + 2);
1050     HB_INIT_STACKARRAY(hb_uint32, apply, item->num_glyphs);
1051
1052     uc = item->string + item->item.pos;
1053
1054     properties = props;
1055     f = 0;
1056     l = item->item.length;
1057     if (item->item.pos > 0) {
1058         --f;
1059         ++l;
1060         ++properties;
1061     }
1062     if (f + l + item->item.pos < item->stringLength) {
1063         ++l;
1064     }
1065     if (item->item.script == HB_Script_Nko)
1066         getNkoProperties(uc+f, l, props);
1067     else
1068         getArabicProperties(uc+f, l, props);
1069
1070     for (i = 0; i < (int)item->num_glyphs; i++) {
1071         apply[i] = 0;
1072
1073         if (properties[i].shape == XIsolated)
1074             apply[i] |= MediProperty|FinaProperty|InitProperty;
1075         else if (properties[i].shape == XMedial)
1076             apply[i] |= IsolProperty|FinaProperty|InitProperty;
1077         else if (properties[i].shape == XFinal)
1078             apply[i] |= IsolProperty|MediProperty|InitProperty;
1079         else if (properties[i].shape == XInitial)
1080             apply[i] |= IsolProperty|MediProperty|FinaProperty;
1081
1082         item->attributes[i].justification = properties[i].justification;
1083     }
1084
1085     HB_FREE_STACKARRAY(props);
1086
1087     shaped = HB_OpenTypeShape(item, apply);
1088
1089     HB_FREE_STACKARRAY(apply);
1090
1091     if (!shaped) {
1092         *ot_ok = FALSE;
1093         return FALSE;
1094     }
1095     return HB_OpenTypePosition(item, nglyphs, /*doLogClusters*/TRUE);
1096 }
1097
1098 #endif
1099
1100 /* #### stil missing: identify invalid character combinations */
1101 HB_Bool HB_ArabicShape(HB_ShaperItem *item)
1102 {
1103     int slen;
1104     HB_Bool haveGlyphs;
1105     HB_STACKARRAY(HB_UChar16, shapedChars, item->item.length);
1106
1107     assert(item->item.script == HB_Script_Arabic || item->item.script == HB_Script_Syriac
1108            || item->item.script == HB_Script_Nko);
1109
1110 #ifndef NO_OPENTYPE
1111
1112     if (HB_SelectScript(item, item->item.script == HB_Script_Arabic ? arabic_features : syriac_features)) {
1113         HB_Bool ot_ok;
1114         if (arabicSyriacOpenTypeShape(item, &ot_ok)) {
1115             HB_FREE_STACKARRAY(shapedChars);
1116             return TRUE;
1117         }
1118         if (ot_ok) {
1119             HB_FREE_STACKARRAY(shapedChars);
1120             return FALSE;
1121             /* fall through to the non OT code*/
1122         }
1123     }
1124 #endif
1125
1126     if (item->item.script != HB_Script_Arabic) {
1127         HB_FREE_STACKARRAY(shapedChars);
1128         return HB_BasicShape(item);
1129     }
1130
1131     shapedString(item->string, item->stringLength, item->item.pos, item->item.length, shapedChars, &slen,
1132                   item->item.bidiLevel % 2,
1133                   item->attributes, item->log_clusters);
1134
1135     haveGlyphs = item->font->klass
1136         ->convertStringToGlyphIndices(item->font,
1137                                       shapedChars, slen,
1138                                       item->glyphs, &item->num_glyphs,
1139                                       item->item.bidiLevel % 2);
1140
1141     HB_FREE_STACKARRAY(shapedChars);
1142
1143     if (!haveGlyphs)
1144         return FALSE;
1145
1146     HB_HeuristicPosition(item);
1147     return TRUE;
1148 }
1149
1150