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