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