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 
arabicGroup(unsigned short uc)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 
getArabicProperties(const unsigned short * chars,int len,HB_ArabicProperties * properties)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 
getNkoJoining(unsigned short uc)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 
getNkoProperties(const unsigned short * chars,int len,HB_ArabicProperties * properties)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 
getShape(hb_uint8 cell,int shape)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 */
prevChar(const HB_UChar16 * str,int pos)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 
nextChar(const HB_UChar16 * str,hb_uint32 len,hb_uint32 pos)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 
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)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 
arabicSyriacOpenTypeShape(HB_ShaperItem * item,HB_Bool * ot_ok)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 */
HB_ArabicShape(HB_ShaperItem * item)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 
1151