rsaenh: Simplify store_key_container_permissions.
[wine] / dlls / usp10 / shape.c
1 /*
2  * Implementation of Shaping for the Uniscribe Script Processor (usp10.dll)
3  *
4  * Copyright 2010 CodeWeavers, Aric Stewart
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  */
21 #include <stdarg.h>
22
23 #include "windef.h"
24 #include "winbase.h"
25 #include "wingdi.h"
26 #include "winuser.h"
27 #include "winnls.h"
28 #include "usp10.h"
29 #include "winternl.h"
30
31 #include "usp10_internal.h"
32
33 #include "wine/debug.h"
34
35 WINE_DEFAULT_DEBUG_CHANNEL(uniscribe);
36
37 #define FIRST_ARABIC_CHAR 0x0600
38 #define LAST_ARABIC_CHAR  0x06ff
39
40 extern const unsigned short wine_shaping_table[];
41 extern const unsigned short wine_shaping_forms[LAST_ARABIC_CHAR - FIRST_ARABIC_CHAR + 1][4];
42
43 enum joining_types {
44     jtU,
45     jtT,
46     jtR,
47     jtL,
48     jtD,
49     jtC
50 };
51
52 enum joined_forms {
53     Xn=0,
54     Xr,
55     Xl,
56     Xm
57 };
58
59 #ifdef WORDS_BIGENDIAN
60 #define GET_BE_WORD(x) (x)
61 #else
62 #define GET_BE_WORD(x) RtlUshortByteSwap(x)
63 #endif
64
65 /* These are all structures needed for the GSUB table */
66 #define MS_MAKE_TAG( _x1, _x2, _x3, _x4 ) \
67           ( ( (ULONG)_x4 << 24 ) |     \
68             ( (ULONG)_x3 << 16 ) |     \
69             ( (ULONG)_x2 <<  8 ) |     \
70               (ULONG)_x1         )
71
72 #define GSUB_TAG MS_MAKE_TAG('G', 'S', 'U', 'B')
73 #define GSUB_E_NOFEATURE -2
74 #define GSUB_E_NOGLYPH -1
75
76 typedef struct {
77     DWORD version;
78     WORD ScriptList;
79     WORD FeatureList;
80     WORD LookupList;
81 } GSUB_Header;
82
83 typedef struct {
84     CHAR ScriptTag[4];
85     WORD Script;
86 } GSUB_ScriptRecord;
87
88 typedef struct {
89     WORD ScriptCount;
90     GSUB_ScriptRecord ScriptRecord[1];
91 } GSUB_ScriptList;
92
93 typedef struct {
94     CHAR LangSysTag[4];
95     WORD LangSys;
96 } GSUB_LangSysRecord;
97
98 typedef struct {
99     WORD DefaultLangSys;
100     WORD LangSysCount;
101     GSUB_LangSysRecord LangSysRecord[1];
102 } GSUB_Script;
103
104 typedef struct {
105     WORD LookupOrder; /* Reserved */
106     WORD ReqFeatureIndex;
107     WORD FeatureCount;
108     WORD FeatureIndex[1];
109 } GSUB_LangSys;
110
111 typedef struct {
112     CHAR FeatureTag[4];
113     WORD Feature;
114 } GSUB_FeatureRecord;
115
116 typedef struct {
117     WORD FeatureCount;
118     GSUB_FeatureRecord FeatureRecord[1];
119 } GSUB_FeatureList;
120
121 typedef struct {
122     WORD FeatureParams; /* Reserved */
123     WORD LookupCount;
124     WORD LookupListIndex[1];
125 } GSUB_Feature;
126
127 typedef struct {
128     WORD LookupCount;
129     WORD Lookup[1];
130 } GSUB_LookupList;
131
132 typedef struct {
133     WORD LookupType;
134     WORD LookupFlag;
135     WORD SubTableCount;
136     WORD SubTable[1];
137 } GSUB_LookupTable;
138
139 typedef struct {
140     WORD CoverageFormat;
141     WORD GlyphCount;
142     WORD GlyphArray[1];
143 } GSUB_CoverageFormat1;
144
145 typedef struct {
146     WORD Start;
147     WORD End;
148     WORD StartCoverageIndex;
149 } GSUB_RangeRecord;
150
151 typedef struct {
152     WORD CoverageFormat;
153     WORD RangeCount;
154     GSUB_RangeRecord RangeRecord[1];
155 } GSUB_CoverageFormat2;
156
157 typedef struct {
158     WORD SubstFormat; /* = 1 */
159     WORD Coverage;
160     WORD DeltaGlyphID;
161 } GSUB_SingleSubstFormat1;
162
163 typedef struct {
164     WORD SubstFormat; /* = 2 */
165     WORD Coverage;
166     WORD GlyphCount;
167     WORD Substitute[1];
168 }GSUB_SingleSubstFormat2;
169
170 typedef struct {
171     WORD SubstFormat; /* = 1 */
172     WORD Coverage;
173     WORD LigSetCount;
174     WORD LigatureSet[1];
175 }GSUB_LigatureSubstFormat1;
176
177 typedef struct {
178     WORD LigatureCount;
179     WORD Ligature[1];
180 }GSUB_LigatureSet;
181
182 typedef struct{
183     WORD LigGlyph;
184     WORD CompCount;
185     WORD Component[1];
186 }GSUB_Ligature;
187
188 /* the orders of joined_forms and contextual_features need to line up */
189 static const char* contextual_features[] =
190 {
191     "isol",
192     "fina",
193     "init",
194     "medi"
195 };
196
197 static INT GSUB_is_glyph_covered(LPCVOID table , UINT glyph)
198 {
199     const GSUB_CoverageFormat1* cf1;
200
201     cf1 = table;
202
203     if (GET_BE_WORD(cf1->CoverageFormat) == 1)
204     {
205         int count = GET_BE_WORD(cf1->GlyphCount);
206         int i;
207         TRACE("Coverage Format 1, %i glyphs\n",count);
208         for (i = 0; i < count; i++)
209             if (glyph == GET_BE_WORD(cf1->GlyphArray[i]))
210                 return i;
211         return -1;
212     }
213     else if (GET_BE_WORD(cf1->CoverageFormat) == 2)
214     {
215         const GSUB_CoverageFormat2* cf2;
216         int i;
217         int count;
218         cf2 = (const GSUB_CoverageFormat2*)cf1;
219
220         count = GET_BE_WORD(cf2->RangeCount);
221         TRACE("Coverage Format 2, %i ranges\n",count);
222         for (i = 0; i < count; i++)
223         {
224             if (glyph < GET_BE_WORD(cf2->RangeRecord[i].Start))
225                 return -1;
226             if ((glyph >= GET_BE_WORD(cf2->RangeRecord[i].Start)) &&
227                 (glyph <= GET_BE_WORD(cf2->RangeRecord[i].End)))
228             {
229                 return (GET_BE_WORD(cf2->RangeRecord[i].StartCoverageIndex) +
230                     glyph - GET_BE_WORD(cf2->RangeRecord[i].Start));
231             }
232         }
233         return -1;
234     }
235     else
236         ERR("Unknown CoverageFormat %i\n",GET_BE_WORD(cf1->CoverageFormat));
237
238     return -1;
239 }
240
241 static const GSUB_Script* GSUB_get_script_table( const GSUB_Header* header, const char* tag)
242 {
243     const GSUB_ScriptList *script;
244     const GSUB_Script *deflt = NULL;
245     int i;
246     script = (const GSUB_ScriptList*)((const BYTE*)header + GET_BE_WORD(header->ScriptList));
247
248     TRACE("%i scripts in this font\n",GET_BE_WORD(script->ScriptCount));
249     for (i = 0; i < GET_BE_WORD(script->ScriptCount); i++)
250     {
251         const GSUB_Script *scr;
252         int offset;
253
254         offset = GET_BE_WORD(script->ScriptRecord[i].Script);
255         scr = (const GSUB_Script*)((const BYTE*)script + offset);
256
257         if (strncmp(script->ScriptRecord[i].ScriptTag, tag,4)==0)
258             return scr;
259         if (strncmp(script->ScriptRecord[i].ScriptTag, "dflt",4)==0)
260             deflt = scr;
261     }
262     return deflt;
263 }
264
265 static const GSUB_LangSys* GSUB_get_lang_table( const GSUB_Script* script, const char* tag)
266 {
267     int i;
268     int offset;
269     const GSUB_LangSys *Lang;
270
271     TRACE("Deflang %x, LangCount %i\n",GET_BE_WORD(script->DefaultLangSys), GET_BE_WORD(script->LangSysCount));
272
273     for (i = 0; i < GET_BE_WORD(script->LangSysCount) ; i++)
274     {
275         offset = GET_BE_WORD(script->LangSysRecord[i].LangSys);
276         Lang = (const GSUB_LangSys*)((const BYTE*)script + offset);
277
278         if ( strncmp(script->LangSysRecord[i].LangSysTag,tag,4)==0)
279             return Lang;
280     }
281     offset = GET_BE_WORD(script->DefaultLangSys);
282     if (offset)
283     {
284         Lang = (const GSUB_LangSys*)((const BYTE*)script + offset);
285         return Lang;
286     }
287     return NULL;
288 }
289
290 static const GSUB_Feature * GSUB_get_feature(const GSUB_Header *header, const GSUB_LangSys *lang, const char* tag)
291 {
292     int i;
293     const GSUB_FeatureList *feature;
294     feature = (const GSUB_FeatureList*)((const BYTE*)header + GET_BE_WORD(header->FeatureList));
295
296     TRACE("%i features\n",GET_BE_WORD(lang->FeatureCount));
297     for (i = 0; i < GET_BE_WORD(lang->FeatureCount); i++)
298     {
299         int index = GET_BE_WORD(lang->FeatureIndex[i]);
300         if (strncmp(feature->FeatureRecord[index].FeatureTag,tag,4)==0)
301         {
302             const GSUB_Feature *feat;
303             feat = (const GSUB_Feature*)((const BYTE*)feature + GET_BE_WORD(feature->FeatureRecord[index].Feature));
304             return feat;
305         }
306     }
307     return NULL;
308 }
309
310 static INT GSUB_apply_SingleSubst(const GSUB_LookupTable *look, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
311 {
312     int j;
313     TRACE("Single Substitution Subtable\n");
314
315     for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
316     {
317         int offset;
318         const GSUB_SingleSubstFormat1 *ssf1;
319         offset = GET_BE_WORD(look->SubTable[j]);
320         ssf1 = (const GSUB_SingleSubstFormat1*)((const BYTE*)look+offset);
321         if (GET_BE_WORD(ssf1->SubstFormat) == 1)
322         {
323             int offset = GET_BE_WORD(ssf1->Coverage);
324             TRACE("  subtype 1, delta %i\n", GET_BE_WORD(ssf1->DeltaGlyphID));
325             if (GSUB_is_glyph_covered((const BYTE*)ssf1+offset, glyphs[glyph_index]) != -1)
326             {
327                 TRACE("  Glyph 0x%x ->",glyphs[glyph_index]);
328                 glyphs[glyph_index] = glyphs[glyph_index] + GET_BE_WORD(ssf1->DeltaGlyphID);
329                 TRACE(" 0x%x\n",glyphs[glyph_index]);
330                 return glyph_index + 1;
331             }
332         }
333         else
334         {
335             const GSUB_SingleSubstFormat2 *ssf2;
336             INT index;
337             INT offset;
338
339             ssf2 = (const GSUB_SingleSubstFormat2 *)ssf1;
340             offset = GET_BE_WORD(ssf1->Coverage);
341             TRACE("  subtype 2,  glyph count %i\n", GET_BE_WORD(ssf2->GlyphCount));
342             index = GSUB_is_glyph_covered((const BYTE*)ssf2+offset, glyphs[glyph_index]);
343             TRACE("  Coverage index %i\n",index);
344             if (index != -1)
345             {
346                 TRACE("    Glyph is 0x%x ->",glyphs[glyph_index]);
347                 glyphs[glyph_index] = GET_BE_WORD(ssf2->Substitute[index]);
348                 TRACE("0x%x\n",glyphs[glyph_index]);
349                 return glyph_index + 1;
350             }
351         }
352     }
353     return GSUB_E_NOGLYPH;
354 }
355
356 static INT GSUB_apply_LigatureSubst(const GSUB_LookupTable *look, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
357 {
358     int j;
359
360     TRACE("Ligature Substitution Subtable\n");
361     for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
362     {
363         const GSUB_LigatureSubstFormat1 *lsf1;
364         int offset,index;
365
366         offset = GET_BE_WORD(look->SubTable[j]);
367         lsf1 = (const GSUB_LigatureSubstFormat1*)((const BYTE*)look+offset);
368         offset = GET_BE_WORD(lsf1->Coverage);
369         index = GSUB_is_glyph_covered((const BYTE*)lsf1+offset, glyphs[glyph_index]);
370         TRACE("  Coverage index %i\n",index);
371         if (index != -1)
372         {
373             const GSUB_LigatureSet *ls;
374             int k, count;
375
376             offset = GET_BE_WORD(lsf1->LigatureSet[index]);
377             ls = (const GSUB_LigatureSet*)((const BYTE*)lsf1+offset);
378             count = GET_BE_WORD(ls->LigatureCount);
379             TRACE("  LigatureSet has %i members\n",count);
380             for (k = 0; k < count; k++)
381             {
382                 const GSUB_Ligature *lig;
383                 int CompCount,l,CompIndex;
384
385                 offset = GET_BE_WORD(ls->Ligature[k]);
386                 lig = (const GSUB_Ligature*)((const BYTE*)ls+offset);
387                 CompCount = GET_BE_WORD(lig->CompCount) - 1;
388                 CompIndex = glyph_index+write_dir;
389                 for (l = 0; l < CompCount && CompIndex >= 0 && CompIndex < *glyph_count; l++)
390                 {
391                     int CompGlyph;
392                     CompGlyph = GET_BE_WORD(lig->Component[l]);
393                     if (CompGlyph != glyphs[CompIndex])
394                         break;
395                     CompIndex += write_dir;
396                 }
397                 if (l == CompCount)
398                 {
399                     int replaceIdx = glyph_index;
400                     if (write_dir < 0)
401                         replaceIdx = glyph_index - CompCount;
402
403                     TRACE("    Glyph is 0x%x (+%i) ->",glyphs[glyph_index],CompCount);
404                     glyphs[replaceIdx] = GET_BE_WORD(lig->LigGlyph);
405                     TRACE("0x%x\n",glyphs[replaceIdx]);
406                     if (CompCount > 0)
407                     {
408                         int j;
409                         for (j = replaceIdx + 1; j < *glyph_count; j++)
410                             glyphs[j] =glyphs[j+CompCount];
411                         *glyph_count = *glyph_count - CompCount;
412                     }
413                     return replaceIdx + 1;
414                 }
415             }
416         }
417     }
418     return GSUB_E_NOGLYPH;
419 }
420
421 static INT GSUB_apply_lookup(const GSUB_LookupList* lookup, INT lookup_index, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
422 {
423     int offset;
424     const GSUB_LookupTable *look;
425
426     offset = GET_BE_WORD(lookup->Lookup[lookup_index]);
427     look = (const GSUB_LookupTable*)((const BYTE*)lookup + offset);
428     TRACE("type %i, flag %x, subtables %i\n",GET_BE_WORD(look->LookupType),GET_BE_WORD(look->LookupFlag),GET_BE_WORD(look->SubTableCount));
429     switch(GET_BE_WORD(look->LookupType))
430     {
431         case 1:
432             return GSUB_apply_SingleSubst(look, glyphs, glyph_index, write_dir, glyph_count);
433         case 4:
434             return GSUB_apply_LigatureSubst(look, glyphs, glyph_index, write_dir, glyph_count);
435         default:
436             FIXME("We do not handle SubType %i\n",GET_BE_WORD(look->LookupType));
437     }
438     return GSUB_E_NOGLYPH;
439 }
440
441 static INT GSUB_apply_feature(const GSUB_Header * header, const GSUB_Feature* feature, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
442 {
443     int i;
444     int out_index = GSUB_E_NOGLYPH;
445     const GSUB_LookupList *lookup;
446
447     lookup = (const GSUB_LookupList*)((const BYTE*)header + GET_BE_WORD(header->LookupList));
448
449     TRACE("%i lookups\n", GET_BE_WORD(feature->LookupCount));
450     for (i = 0; i < GET_BE_WORD(feature->LookupCount); i++)
451     {
452         out_index = GSUB_apply_lookup(lookup, GET_BE_WORD(feature->LookupListIndex[i]), glyphs, glyph_index, write_dir, glyph_count);
453         if (out_index != GSUB_E_NOGLYPH)
454             break;
455     }
456     if (out_index == GSUB_E_NOGLYPH)
457         TRACE("lookups found no glyphs\n");
458     return out_index;
459 }
460
461 static const char* get_opentype_script(HDC hdc, SCRIPT_ANALYSIS *psa)
462 {
463     UINT charset;
464
465     switch (psa->eScript)
466     {
467         case Script_Arabic:
468             return "arab";
469         case Script_Syriac:
470             return "syrc";
471         case Script_Hebrew:
472             return "hebr";
473         case Script_Latin:
474         case Script_Numeric:
475         case Script_CR:
476         case Script_LF:
477             return "latn";
478     }
479
480     /*
481      * fall back to the font charset
482      */
483     charset = GetTextCharsetInfo(hdc, NULL, 0x0);
484     switch (charset)
485     {
486         case ANSI_CHARSET: return "latn";
487         case BALTIC_CHARSET: return "latn"; /* ?? */
488         case CHINESEBIG5_CHARSET: return "hani";
489         case EASTEUROPE_CHARSET: return "latn"; /* ?? */
490         case GB2312_CHARSET: return "hani";
491         case GREEK_CHARSET: return "grek";
492         case HANGUL_CHARSET: return "hang";
493         case RUSSIAN_CHARSET: return "cyrl";
494         case SHIFTJIS_CHARSET: return "kana";
495         case TURKISH_CHARSET: return "latn"; /* ?? */
496         case VIETNAMESE_CHARSET: return "latn";
497         case JOHAB_CHARSET: return "latn"; /* ?? */
498         case ARABIC_CHARSET: return "arab";
499         case HEBREW_CHARSET: return "hebr";
500         case THAI_CHARSET: return "thai";
501         default: return "latn";
502     }
503 }
504
505 static INT apply_GSUB_feature_to_glyph(HDC hdc, SCRIPT_ANALYSIS *psa, void* GSUB_Table, WORD *glyphs, INT index, INT write_dir, INT* glyph_count, const char* feat)
506 {
507     const GSUB_Header *header;
508     const GSUB_Script *script;
509     const GSUB_LangSys *language;
510     const GSUB_Feature *feature;
511
512     if (!GSUB_Table)
513         return GSUB_E_NOFEATURE;
514
515     header = GSUB_Table;
516
517     script = GSUB_get_script_table(header, get_opentype_script(hdc,psa));
518     if (!script)
519     {
520         TRACE("Script not found\n");
521         return GSUB_E_NOFEATURE;
522     }
523     language = GSUB_get_lang_table(script, "xxxx"); /* Need to get Lang tag */
524     if (!language)
525     {
526         TRACE("Language not found\n");
527         return GSUB_E_NOFEATURE;
528     }
529     feature  =  GSUB_get_feature(header, language, feat);
530     if (!feature)
531     {
532         TRACE("%s feature not found\n",feat);
533         return GSUB_E_NOFEATURE;
534     }
535     TRACE("applying feature %s\n",feat);
536     return GSUB_apply_feature(header, feature, glyphs, index, write_dir, glyph_count);
537 }
538
539 static VOID *load_gsub_table(HDC hdc)
540 {
541     VOID* GSUB_Table = NULL;
542     int length = GetFontData(hdc, GSUB_TAG , 0, NULL, 0);
543     if (length != GDI_ERROR)
544     {
545         GSUB_Table = HeapAlloc(GetProcessHeap(),0,length);
546         GetFontData(hdc, GSUB_TAG , 0, GSUB_Table, length);
547         TRACE("Loaded GSUB table of %i bytes\n",length);
548     }
549     return GSUB_Table;
550 }
551
552 static CHAR neighbour_joining_type(int i, int delta, const CHAR* context_type, INT cchLen, SCRIPT_ANALYSIS *psa)
553 {
554     if (i + delta < 0)
555     {
556         if (psa->fLinkBefore)
557             return jtR;
558         else
559             return jtU;
560     }
561     if ( i+ delta >= cchLen)
562     {
563         if (psa->fLinkAfter)
564             return jtL;
565         else
566             return jtU;
567     }
568
569     i += delta;
570
571     if (context_type[i] == jtT)
572         return neighbour_joining_type(i,delta,context_type,cchLen,psa);
573     else
574         return context_type[i];
575 }
576
577 static inline BOOL right_join_causing(CHAR joining_type)
578 {
579     return (joining_type == jtL || joining_type == jtD || joining_type == jtC);
580 }
581
582 static inline BOOL left_join_causing(CHAR joining_type)
583 {
584     return (joining_type == jtR || joining_type == jtD || joining_type == jtC);
585 }
586
587 /* SHAPE_ShapeArabicGlyphs
588  */
589 void SHAPE_ShapeArabicGlyphs(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs)
590 {
591     CHAR *context_type;
592     INT *context_shape;
593     INT dirR, dirL;
594     int i;
595
596     if (psa->eScript != Script_Arabic)
597         return;
598
599     if (*pcGlyphs != cChars)
600     {
601         ERR("Number of Glyphs and Chars need to match at the beginning\n");
602         return;
603     }
604
605
606     if (!psa->fLogicalOrder && psa->fRTL)
607     {
608         dirR = 1;
609         dirL = -1;
610     }
611     else
612     {
613         dirR = -1;
614         dirL = 1;
615     }
616
617     if (!psc->GSUB_Table)
618         psc->GSUB_Table = load_gsub_table(hdc);
619
620     context_type = HeapAlloc(GetProcessHeap(),0,cChars);
621     context_shape = HeapAlloc(GetProcessHeap(),0,sizeof(INT) * cChars);
622
623     for (i = 0; i < cChars; i++)
624         context_type[i] = wine_shaping_table[wine_shaping_table[pwcChars[i] >> 8] + (pwcChars[i] & 0xff)];
625
626     for (i = 0; i < cChars; i++)
627     {
628         if (context_type[i] == jtR && right_join_causing(neighbour_joining_type(i,dirR,context_type,cChars,psa)))
629             context_shape[i] = Xr;
630         else if (context_type[i] == jtL && left_join_causing(neighbour_joining_type(i,dirL,context_type,cChars,psa)))
631             context_shape[i] = Xl;
632         else if (context_type[i] == jtD && left_join_causing(neighbour_joining_type(i,dirL,context_type,cChars,psa)) && right_join_causing(neighbour_joining_type(i,dirR,context_type,cChars,psa)))
633             context_shape[i] = Xm;
634         else if (context_type[i] == jtD && right_join_causing(neighbour_joining_type(i,dirR,context_type,cChars,psa)))
635             context_shape[i] = Xr;
636         else if (context_type[i] == jtD && left_join_causing(neighbour_joining_type(i,dirL,context_type,cChars,psa)))
637             context_shape[i] = Xl;
638         else
639             context_shape[i] = Xn;
640     }
641
642     /* Contextual Shaping */
643     i = 0;
644     while(i < *pcGlyphs)
645     {
646         BOOL shaped = FALSE;
647
648         if (psc->GSUB_Table)
649         {
650             INT nextIndex;
651             nextIndex = apply_GSUB_feature_to_glyph(hdc, psa, psc->GSUB_Table, pwOutGlyphs, i, dirL, pcGlyphs, contextual_features[context_shape[i]]);
652             if (nextIndex > GSUB_E_NOGLYPH)
653                 i = nextIndex;
654             shaped = (nextIndex > GSUB_E_NOGLYPH);
655         }
656
657         if (!shaped)
658         {
659             WORD newGlyph = pwOutGlyphs[i];
660             if (pwcChars[i] >= FIRST_ARABIC_CHAR && pwcChars[i] <= LAST_ARABIC_CHAR)
661             {
662                 /* fall back to presentation form B */
663                 WCHAR context_char = wine_shaping_forms[pwcChars[i] - FIRST_ARABIC_CHAR][context_shape[i]];
664                 if (context_char != pwcChars[i] && GetGlyphIndicesW(hdc, &context_char, 1, &newGlyph, 0) != GDI_ERROR && newGlyph != 0x0000)
665                     pwOutGlyphs[i] = newGlyph;
666             }
667             i++;
668         }
669     }
670
671     /* Required ligature substitution */
672     if (psc->GSUB_Table)
673     {
674         i = 0;
675         while(i < *pcGlyphs)
676         {
677                 INT nextIndex;
678                 nextIndex = apply_GSUB_feature_to_glyph(hdc, psa, psc->GSUB_Table, pwOutGlyphs, i, dirL, pcGlyphs, "rlig");
679                 if (nextIndex > GSUB_E_NOGLYPH)
680                     i = nextIndex;
681                 else if (nextIndex == GSUB_E_NOFEATURE)
682                     break;
683                 else
684                     i++;
685         }
686     }
687
688     HeapFree(GetProcessHeap(),0,context_shape);
689     HeapFree(GetProcessHeap(),0,context_type);
690 }