ddraw: Use a less offensive handle table implementation for matrices.
[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 typedef struct{
189     WORD SequenceIndex;
190     WORD LookupListIndex;
191
192 }GSUB_SubstLookupRecord;
193
194 typedef struct{
195     WORD SubstFormat; /* = 1 */
196     WORD Coverage;
197     WORD ChainSubRuleSetCount;
198     WORD ChainSubRuleSet[1];
199 }GSUB_ChainContextSubstFormat1;
200
201 typedef struct {
202     WORD SubstFormat; /* = 3 */
203     WORD BacktrackGlyphCount;
204     WORD Coverage[1];
205 }GSUB_ChainContextSubstFormat3_1;
206
207 typedef struct{
208     WORD InputGlyphCount;
209     WORD Coverage[1];
210 }GSUB_ChainContextSubstFormat3_2;
211
212 typedef struct{
213     WORD LookaheadGlyphCount;
214     WORD Coverage[1];
215 }GSUB_ChainContextSubstFormat3_3;
216
217 typedef struct{
218     WORD SubstCount;
219     GSUB_SubstLookupRecord SubstLookupRecord[1];
220 }GSUB_ChainContextSubstFormat3_4;
221
222 static INT GSUB_apply_lookup(const GSUB_LookupList* lookup, INT lookup_index, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count);
223
224 /* the orders of joined_forms and contextual_features need to line up */
225 static const char* contextual_features[] =
226 {
227     "isol",
228     "fina",
229     "init",
230     "medi"
231 };
232
233 static const char* arabic_GSUB_features[] =
234 {
235     "rlig",
236     "calt",
237     "liga",
238     "dlig",
239     "cswh",
240     "mset",
241     NULL
242 };
243
244 static INT GSUB_is_glyph_covered(LPCVOID table , UINT glyph)
245 {
246     const GSUB_CoverageFormat1* cf1;
247
248     cf1 = table;
249
250     if (GET_BE_WORD(cf1->CoverageFormat) == 1)
251     {
252         int count = GET_BE_WORD(cf1->GlyphCount);
253         int i;
254         TRACE("Coverage Format 1, %i glyphs\n",count);
255         for (i = 0; i < count; i++)
256             if (glyph == GET_BE_WORD(cf1->GlyphArray[i]))
257                 return i;
258         return -1;
259     }
260     else if (GET_BE_WORD(cf1->CoverageFormat) == 2)
261     {
262         const GSUB_CoverageFormat2* cf2;
263         int i;
264         int count;
265         cf2 = (const GSUB_CoverageFormat2*)cf1;
266
267         count = GET_BE_WORD(cf2->RangeCount);
268         TRACE("Coverage Format 2, %i ranges\n",count);
269         for (i = 0; i < count; i++)
270         {
271             if (glyph < GET_BE_WORD(cf2->RangeRecord[i].Start))
272                 return -1;
273             if ((glyph >= GET_BE_WORD(cf2->RangeRecord[i].Start)) &&
274                 (glyph <= GET_BE_WORD(cf2->RangeRecord[i].End)))
275             {
276                 return (GET_BE_WORD(cf2->RangeRecord[i].StartCoverageIndex) +
277                     glyph - GET_BE_WORD(cf2->RangeRecord[i].Start));
278             }
279         }
280         return -1;
281     }
282     else
283         ERR("Unknown CoverageFormat %i\n",GET_BE_WORD(cf1->CoverageFormat));
284
285     return -1;
286 }
287
288 static const GSUB_Script* GSUB_get_script_table( const GSUB_Header* header, const char* tag)
289 {
290     const GSUB_ScriptList *script;
291     const GSUB_Script *deflt = NULL;
292     int i;
293     script = (const GSUB_ScriptList*)((const BYTE*)header + GET_BE_WORD(header->ScriptList));
294
295     TRACE("%i scripts in this font\n",GET_BE_WORD(script->ScriptCount));
296     for (i = 0; i < GET_BE_WORD(script->ScriptCount); i++)
297     {
298         const GSUB_Script *scr;
299         int offset;
300
301         offset = GET_BE_WORD(script->ScriptRecord[i].Script);
302         scr = (const GSUB_Script*)((const BYTE*)script + offset);
303
304         if (strncmp(script->ScriptRecord[i].ScriptTag, tag,4)==0)
305             return scr;
306         if (strncmp(script->ScriptRecord[i].ScriptTag, "dflt",4)==0)
307             deflt = scr;
308     }
309     return deflt;
310 }
311
312 static const GSUB_LangSys* GSUB_get_lang_table( const GSUB_Script* script, const char* tag)
313 {
314     int i;
315     int offset;
316     const GSUB_LangSys *Lang;
317
318     TRACE("Deflang %x, LangCount %i\n",GET_BE_WORD(script->DefaultLangSys), GET_BE_WORD(script->LangSysCount));
319
320     for (i = 0; i < GET_BE_WORD(script->LangSysCount) ; i++)
321     {
322         offset = GET_BE_WORD(script->LangSysRecord[i].LangSys);
323         Lang = (const GSUB_LangSys*)((const BYTE*)script + offset);
324
325         if ( strncmp(script->LangSysRecord[i].LangSysTag,tag,4)==0)
326             return Lang;
327     }
328     offset = GET_BE_WORD(script->DefaultLangSys);
329     if (offset)
330     {
331         Lang = (const GSUB_LangSys*)((const BYTE*)script + offset);
332         return Lang;
333     }
334     return NULL;
335 }
336
337 static const GSUB_Feature * GSUB_get_feature(const GSUB_Header *header, const GSUB_LangSys *lang, const char* tag)
338 {
339     int i;
340     const GSUB_FeatureList *feature;
341     feature = (const GSUB_FeatureList*)((const BYTE*)header + GET_BE_WORD(header->FeatureList));
342
343     TRACE("%i features\n",GET_BE_WORD(lang->FeatureCount));
344     for (i = 0; i < GET_BE_WORD(lang->FeatureCount); i++)
345     {
346         int index = GET_BE_WORD(lang->FeatureIndex[i]);
347         if (strncmp(feature->FeatureRecord[index].FeatureTag,tag,4)==0)
348         {
349             const GSUB_Feature *feat;
350             feat = (const GSUB_Feature*)((const BYTE*)feature + GET_BE_WORD(feature->FeatureRecord[index].Feature));
351             return feat;
352         }
353     }
354     return NULL;
355 }
356
357 static INT GSUB_apply_SingleSubst(const GSUB_LookupTable *look, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
358 {
359     int j;
360     TRACE("Single Substitution Subtable\n");
361
362     for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
363     {
364         int offset;
365         const GSUB_SingleSubstFormat1 *ssf1;
366         offset = GET_BE_WORD(look->SubTable[j]);
367         ssf1 = (const GSUB_SingleSubstFormat1*)((const BYTE*)look+offset);
368         if (GET_BE_WORD(ssf1->SubstFormat) == 1)
369         {
370             int offset = GET_BE_WORD(ssf1->Coverage);
371             TRACE("  subtype 1, delta %i\n", GET_BE_WORD(ssf1->DeltaGlyphID));
372             if (GSUB_is_glyph_covered((const BYTE*)ssf1+offset, glyphs[glyph_index]) != -1)
373             {
374                 TRACE("  Glyph 0x%x ->",glyphs[glyph_index]);
375                 glyphs[glyph_index] = glyphs[glyph_index] + GET_BE_WORD(ssf1->DeltaGlyphID);
376                 TRACE(" 0x%x\n",glyphs[glyph_index]);
377                 return glyph_index + 1;
378             }
379         }
380         else
381         {
382             const GSUB_SingleSubstFormat2 *ssf2;
383             INT index;
384             INT offset;
385
386             ssf2 = (const GSUB_SingleSubstFormat2 *)ssf1;
387             offset = GET_BE_WORD(ssf1->Coverage);
388             TRACE("  subtype 2,  glyph count %i\n", GET_BE_WORD(ssf2->GlyphCount));
389             index = GSUB_is_glyph_covered((const BYTE*)ssf2+offset, glyphs[glyph_index]);
390             TRACE("  Coverage index %i\n",index);
391             if (index != -1)
392             {
393                 TRACE("    Glyph is 0x%x ->",glyphs[glyph_index]);
394                 glyphs[glyph_index] = GET_BE_WORD(ssf2->Substitute[index]);
395                 TRACE("0x%x\n",glyphs[glyph_index]);
396                 return glyph_index + 1;
397             }
398         }
399     }
400     return GSUB_E_NOGLYPH;
401 }
402
403 static INT GSUB_apply_LigatureSubst(const GSUB_LookupTable *look, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
404 {
405     int j;
406
407     TRACE("Ligature Substitution Subtable\n");
408     for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
409     {
410         const GSUB_LigatureSubstFormat1 *lsf1;
411         int offset,index;
412
413         offset = GET_BE_WORD(look->SubTable[j]);
414         lsf1 = (const GSUB_LigatureSubstFormat1*)((const BYTE*)look+offset);
415         offset = GET_BE_WORD(lsf1->Coverage);
416         index = GSUB_is_glyph_covered((const BYTE*)lsf1+offset, glyphs[glyph_index]);
417         TRACE("  Coverage index %i\n",index);
418         if (index != -1)
419         {
420             const GSUB_LigatureSet *ls;
421             int k, count;
422
423             offset = GET_BE_WORD(lsf1->LigatureSet[index]);
424             ls = (const GSUB_LigatureSet*)((const BYTE*)lsf1+offset);
425             count = GET_BE_WORD(ls->LigatureCount);
426             TRACE("  LigatureSet has %i members\n",count);
427             for (k = 0; k < count; k++)
428             {
429                 const GSUB_Ligature *lig;
430                 int CompCount,l,CompIndex;
431
432                 offset = GET_BE_WORD(ls->Ligature[k]);
433                 lig = (const GSUB_Ligature*)((const BYTE*)ls+offset);
434                 CompCount = GET_BE_WORD(lig->CompCount) - 1;
435                 CompIndex = glyph_index+write_dir;
436                 for (l = 0; l < CompCount && CompIndex >= 0 && CompIndex < *glyph_count; l++)
437                 {
438                     int CompGlyph;
439                     CompGlyph = GET_BE_WORD(lig->Component[l]);
440                     if (CompGlyph != glyphs[CompIndex])
441                         break;
442                     CompIndex += write_dir;
443                 }
444                 if (l == CompCount)
445                 {
446                     int replaceIdx = glyph_index;
447                     if (write_dir < 0)
448                         replaceIdx = glyph_index - CompCount;
449
450                     TRACE("    Glyph is 0x%x (+%i) ->",glyphs[glyph_index],CompCount);
451                     glyphs[replaceIdx] = GET_BE_WORD(lig->LigGlyph);
452                     TRACE("0x%x\n",glyphs[replaceIdx]);
453                     if (CompCount > 0)
454                     {
455                         int j;
456                         for (j = replaceIdx + 1; j < *glyph_count; j++)
457                             glyphs[j] =glyphs[j+CompCount];
458                         *glyph_count = *glyph_count - CompCount;
459                     }
460                     return replaceIdx + 1;
461                 }
462             }
463         }
464     }
465     return GSUB_E_NOGLYPH;
466 }
467
468 static INT GSUB_apply_ChainContextSubst(const GSUB_LookupList* lookup, const GSUB_LookupTable *look, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
469 {
470     int j;
471     BOOL done = FALSE;
472
473     TRACE("Chaining Contextual Substitution Subtable\n");
474     for (j = 0; j < GET_BE_WORD(look->SubTableCount) && !done; j++)
475     {
476         const GSUB_ChainContextSubstFormat1 *ccsf1;
477         int offset;
478         int dirLookahead = write_dir;
479         int dirBacktrack = -1 * write_dir;
480
481         offset = GET_BE_WORD(look->SubTable[j]);
482         ccsf1 = (const GSUB_ChainContextSubstFormat1*)((const BYTE*)look+offset);
483         if (GET_BE_WORD(ccsf1->SubstFormat) == 1)
484         {
485             FIXME("  TODO: subtype 1 (Simple context glyph substitution)\n");
486             return -1;
487         }
488         else if (GET_BE_WORD(ccsf1->SubstFormat) == 2)
489         {
490             FIXME("  TODO: subtype 2 (Class-based Chaining Context Glyph Substitution)\n");
491             return -1;
492         }
493         else if (GET_BE_WORD(ccsf1->SubstFormat) == 3)
494         {
495             int k;
496             int indexGlyphs;
497             const GSUB_ChainContextSubstFormat3_1 *ccsf3_1;
498             const GSUB_ChainContextSubstFormat3_2 *ccsf3_2;
499             const GSUB_ChainContextSubstFormat3_3 *ccsf3_3;
500             const GSUB_ChainContextSubstFormat3_4 *ccsf3_4;
501             int newIndex = glyph_index;
502
503             ccsf3_1 = (const GSUB_ChainContextSubstFormat3_1 *)ccsf1;
504
505             TRACE("  subtype 3 (Coverage-based Chaining Context Glyph Substitution)\n");
506
507             for (k = 0; k < GET_BE_WORD(ccsf3_1->BacktrackGlyphCount); k++)
508             {
509                 offset = GET_BE_WORD(ccsf3_1->Coverage[k]);
510                 if (GSUB_is_glyph_covered((const BYTE*)ccsf3_1+offset, glyphs[glyph_index + (dirBacktrack * (k+1))]) == -1)
511                     break;
512             }
513             if (k != GET_BE_WORD(ccsf3_1->BacktrackGlyphCount))
514                 return -1;
515             TRACE("Matched Backtrack\n");
516
517             ccsf3_2 = (const GSUB_ChainContextSubstFormat3_2 *)(((LPBYTE)ccsf1)+sizeof(GSUB_ChainContextSubstFormat3_1) + (sizeof(WORD) * (GET_BE_WORD(ccsf3_1->BacktrackGlyphCount)-1)));
518
519             indexGlyphs = GET_BE_WORD(ccsf3_2->InputGlyphCount);
520             for (k = 0; k < indexGlyphs; k++)
521             {
522                 offset = GET_BE_WORD(ccsf3_2->Coverage[k]);
523                 if (GSUB_is_glyph_covered((const BYTE*)ccsf3_1+offset, glyphs[glyph_index + (write_dir * k)]) == -1)
524                     break;
525             }
526             if (k != indexGlyphs)
527                 return -1;
528             TRACE("Matched IndexGlyphs\n");
529
530             ccsf3_3 = (const GSUB_ChainContextSubstFormat3_3 *)(((LPBYTE)ccsf3_2)+sizeof(GSUB_ChainContextSubstFormat3_2) + (sizeof(WORD) * (GET_BE_WORD(ccsf3_2->InputGlyphCount)-1)));
531
532             for (k = 0; k < GET_BE_WORD(ccsf3_3->LookaheadGlyphCount); k++)
533             {
534                 offset = GET_BE_WORD(ccsf3_3->Coverage[k]);
535                 if (GSUB_is_glyph_covered((const BYTE*)ccsf3_1+offset, glyphs[glyph_index + (dirLookahead * (indexGlyphs + k+1))]) == -1)
536                     break;
537             }
538             if (k != GET_BE_WORD(ccsf3_3->LookaheadGlyphCount))
539                 return -1;
540             TRACE("Matched LookAhead\n");
541
542             ccsf3_4 = (const GSUB_ChainContextSubstFormat3_4 *)(((LPBYTE)ccsf3_3)+sizeof(GSUB_ChainContextSubstFormat3_3) + (sizeof(WORD) * (GET_BE_WORD(ccsf3_3->LookaheadGlyphCount)-1)));
543
544             for (k = 0; k < GET_BE_WORD(ccsf3_4->SubstCount); k++)
545             {
546                 int lookupIndex = GET_BE_WORD(ccsf3_4->SubstLookupRecord[k].LookupListIndex);
547                 int SequenceIndex = GET_BE_WORD(ccsf3_4->SubstLookupRecord[k].SequenceIndex) * write_dir;
548
549                 TRACE("SUBST: %i -> %i %i\n",k, SequenceIndex, lookupIndex);
550                 newIndex = GSUB_apply_lookup(lookup, lookupIndex, glyphs, glyph_index + SequenceIndex, write_dir, glyph_count);
551                 if (newIndex == -1)
552                 {
553                     ERR("Chain failed to generate a glyph\n");
554                     return -1;
555                 }
556             }
557             return newIndex + 1;
558         }
559     }
560     return -1;
561 }
562
563 static INT GSUB_apply_lookup(const GSUB_LookupList* lookup, INT lookup_index, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
564 {
565     int offset;
566     const GSUB_LookupTable *look;
567
568     offset = GET_BE_WORD(lookup->Lookup[lookup_index]);
569     look = (const GSUB_LookupTable*)((const BYTE*)lookup + offset);
570     TRACE("type %i, flag %x, subtables %i\n",GET_BE_WORD(look->LookupType),GET_BE_WORD(look->LookupFlag),GET_BE_WORD(look->SubTableCount));
571     switch(GET_BE_WORD(look->LookupType))
572     {
573         case 1:
574             return GSUB_apply_SingleSubst(look, glyphs, glyph_index, write_dir, glyph_count);
575         case 4:
576             return GSUB_apply_LigatureSubst(look, glyphs, glyph_index, write_dir, glyph_count);
577         case 6:
578             return GSUB_apply_ChainContextSubst(lookup, look, glyphs, glyph_index, write_dir, glyph_count);
579         default:
580             FIXME("We do not handle SubType %i\n",GET_BE_WORD(look->LookupType));
581     }
582     return GSUB_E_NOGLYPH;
583 }
584
585 static INT GSUB_apply_feature(const GSUB_Header * header, const GSUB_Feature* feature, WORD *glyphs, INT glyph_index, INT write_dir, INT *glyph_count)
586 {
587     int i;
588     int out_index = GSUB_E_NOGLYPH;
589     const GSUB_LookupList *lookup;
590
591     lookup = (const GSUB_LookupList*)((const BYTE*)header + GET_BE_WORD(header->LookupList));
592
593     TRACE("%i lookups\n", GET_BE_WORD(feature->LookupCount));
594     for (i = 0; i < GET_BE_WORD(feature->LookupCount); i++)
595     {
596         out_index = GSUB_apply_lookup(lookup, GET_BE_WORD(feature->LookupListIndex[i]), glyphs, glyph_index, write_dir, glyph_count);
597         if (out_index != GSUB_E_NOGLYPH)
598             break;
599     }
600     if (out_index == GSUB_E_NOGLYPH)
601         TRACE("lookups found no glyphs\n");
602     return out_index;
603 }
604
605 static const char* get_opentype_script(HDC hdc, SCRIPT_ANALYSIS *psa)
606 {
607     UINT charset;
608
609     switch (psa->eScript)
610     {
611         case Script_Arabic:
612             return "arab";
613         case Script_Syriac:
614             return "syrc";
615         case Script_Hebrew:
616             return "hebr";
617         case Script_Latin:
618         case Script_Numeric:
619         case Script_CR:
620         case Script_LF:
621             return "latn";
622     }
623
624     /*
625      * fall back to the font charset
626      */
627     charset = GetTextCharsetInfo(hdc, NULL, 0x0);
628     switch (charset)
629     {
630         case ANSI_CHARSET: return "latn";
631         case BALTIC_CHARSET: return "latn"; /* ?? */
632         case CHINESEBIG5_CHARSET: return "hani";
633         case EASTEUROPE_CHARSET: return "latn"; /* ?? */
634         case GB2312_CHARSET: return "hani";
635         case GREEK_CHARSET: return "grek";
636         case HANGUL_CHARSET: return "hang";
637         case RUSSIAN_CHARSET: return "cyrl";
638         case SHIFTJIS_CHARSET: return "kana";
639         case TURKISH_CHARSET: return "latn"; /* ?? */
640         case VIETNAMESE_CHARSET: return "latn";
641         case JOHAB_CHARSET: return "latn"; /* ?? */
642         case ARABIC_CHARSET: return "arab";
643         case HEBREW_CHARSET: return "hebr";
644         case THAI_CHARSET: return "thai";
645         default: return "latn";
646     }
647 }
648
649 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)
650 {
651     const GSUB_Header *header;
652     const GSUB_Script *script;
653     const GSUB_LangSys *language;
654     const GSUB_Feature *feature;
655
656     if (!GSUB_Table)
657         return GSUB_E_NOFEATURE;
658
659     header = GSUB_Table;
660
661     script = GSUB_get_script_table(header, get_opentype_script(hdc,psa));
662     if (!script)
663     {
664         TRACE("Script not found\n");
665         return GSUB_E_NOFEATURE;
666     }
667     language = GSUB_get_lang_table(script, "xxxx"); /* Need to get Lang tag */
668     if (!language)
669     {
670         TRACE("Language not found\n");
671         return GSUB_E_NOFEATURE;
672     }
673     feature  =  GSUB_get_feature(header, language, feat);
674     if (!feature)
675     {
676         TRACE("%s feature not found\n",feat);
677         return GSUB_E_NOFEATURE;
678     }
679     TRACE("applying feature %s\n",feat);
680     return GSUB_apply_feature(header, feature, glyphs, index, write_dir, glyph_count);
681 }
682
683 static VOID *load_gsub_table(HDC hdc)
684 {
685     VOID* GSUB_Table = NULL;
686     int length = GetFontData(hdc, GSUB_TAG , 0, NULL, 0);
687     if (length != GDI_ERROR)
688     {
689         GSUB_Table = HeapAlloc(GetProcessHeap(),0,length);
690         GetFontData(hdc, GSUB_TAG , 0, GSUB_Table, length);
691         TRACE("Loaded GSUB table of %i bytes\n",length);
692     }
693     return GSUB_Table;
694 }
695
696 static int apply_GSUB_feature(HDC hdc, SCRIPT_ANALYSIS *psa, void* GSUB_Table, WORD *pwOutGlyphs, int write_dir, INT* pcGlyphs, const char* feat)
697 {
698     int i;
699
700     if (GSUB_Table)
701     {
702         const GSUB_Header *header;
703         const GSUB_Script *script;
704         const GSUB_LangSys *language;
705         const GSUB_Feature *feature;
706
707         if (!GSUB_Table)
708             return GSUB_E_NOFEATURE;
709
710         header = GSUB_Table;
711
712         script = GSUB_get_script_table(header, get_opentype_script(hdc,psa));
713         if (!script)
714         {
715             TRACE("Script not found\n");
716             return GSUB_E_NOFEATURE;
717         }
718         language = GSUB_get_lang_table(script, "xxxx");
719         if (!language)
720         {
721             TRACE("Language not found\n");
722             return GSUB_E_NOFEATURE;
723         }
724         feature  =  GSUB_get_feature(header, language, feat);
725         if (!feature)
726         {
727             TRACE("%s feature not found\n",feat);
728             return GSUB_E_NOFEATURE;
729         }
730
731         i = 0;
732         TRACE("applying feature %s\n",feat);
733         while(i < *pcGlyphs)
734         {
735                 INT nextIndex;
736                 nextIndex = GSUB_apply_feature(header, feature, pwOutGlyphs, i, write_dir, pcGlyphs);
737                 if (nextIndex > GSUB_E_NOGLYPH)
738                     i = nextIndex;
739                 else
740                     i++;
741         }
742         return *pcGlyphs;
743     }
744     return GSUB_E_NOFEATURE;
745 }
746
747 static CHAR neighbour_joining_type(int i, int delta, const CHAR* context_type, INT cchLen, SCRIPT_ANALYSIS *psa)
748 {
749     if (i + delta < 0)
750     {
751         if (psa->fLinkBefore)
752             return jtR;
753         else
754             return jtU;
755     }
756     if ( i+ delta >= cchLen)
757     {
758         if (psa->fLinkAfter)
759             return jtL;
760         else
761             return jtU;
762     }
763
764     i += delta;
765
766     if (context_type[i] == jtT)
767         return neighbour_joining_type(i,delta,context_type,cchLen,psa);
768     else
769         return context_type[i];
770 }
771
772 static inline BOOL right_join_causing(CHAR joining_type)
773 {
774     return (joining_type == jtL || joining_type == jtD || joining_type == jtC);
775 }
776
777 static inline BOOL left_join_causing(CHAR joining_type)
778 {
779     return (joining_type == jtR || joining_type == jtD || joining_type == jtC);
780 }
781
782 /* SHAPE_ShapeArabicGlyphs
783  */
784 void SHAPE_ShapeArabicGlyphs(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT* pcGlyphs, INT cMaxGlyphs)
785 {
786     CHAR *context_type;
787     INT *context_shape;
788     INT dirR, dirL;
789     int i;
790
791     if (psa->eScript != Script_Arabic)
792         return;
793
794     if (*pcGlyphs != cChars)
795     {
796         ERR("Number of Glyphs and Chars need to match at the beginning\n");
797         return;
798     }
799
800
801     if (!psa->fLogicalOrder && psa->fRTL)
802     {
803         dirR = 1;
804         dirL = -1;
805     }
806     else
807     {
808         dirR = -1;
809         dirL = 1;
810     }
811
812     if (!psc->GSUB_Table)
813         psc->GSUB_Table = load_gsub_table(hdc);
814
815     context_type = HeapAlloc(GetProcessHeap(),0,cChars);
816     context_shape = HeapAlloc(GetProcessHeap(),0,sizeof(INT) * cChars);
817
818     for (i = 0; i < cChars; i++)
819         context_type[i] = wine_shaping_table[wine_shaping_table[pwcChars[i] >> 8] + (pwcChars[i] & 0xff)];
820
821     for (i = 0; i < cChars; i++)
822     {
823         if (context_type[i] == jtR && right_join_causing(neighbour_joining_type(i,dirR,context_type,cChars,psa)))
824             context_shape[i] = Xr;
825         else if (context_type[i] == jtL && left_join_causing(neighbour_joining_type(i,dirL,context_type,cChars,psa)))
826             context_shape[i] = Xl;
827         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)))
828             context_shape[i] = Xm;
829         else if (context_type[i] == jtD && right_join_causing(neighbour_joining_type(i,dirR,context_type,cChars,psa)))
830             context_shape[i] = Xr;
831         else if (context_type[i] == jtD && left_join_causing(neighbour_joining_type(i,dirL,context_type,cChars,psa)))
832             context_shape[i] = Xl;
833         else
834             context_shape[i] = Xn;
835     }
836
837     /* Contextual Shaping */
838     i = 0;
839     while(i < *pcGlyphs)
840     {
841         BOOL shaped = FALSE;
842
843         if (psc->GSUB_Table)
844         {
845             INT nextIndex;
846             nextIndex = apply_GSUB_feature_to_glyph(hdc, psa, psc->GSUB_Table, pwOutGlyphs, i, dirL, pcGlyphs, contextual_features[context_shape[i]]);
847             if (nextIndex > GSUB_E_NOGLYPH)
848                 i = nextIndex;
849             shaped = (nextIndex > GSUB_E_NOGLYPH);
850         }
851
852         if (!shaped)
853         {
854             WORD newGlyph = pwOutGlyphs[i];
855             if (pwcChars[i] >= FIRST_ARABIC_CHAR && pwcChars[i] <= LAST_ARABIC_CHAR)
856             {
857                 /* fall back to presentation form B */
858                 WCHAR context_char = wine_shaping_forms[pwcChars[i] - FIRST_ARABIC_CHAR][context_shape[i]];
859                 if (context_char != pwcChars[i] && GetGlyphIndicesW(hdc, &context_char, 1, &newGlyph, 0) != GDI_ERROR && newGlyph != 0x0000)
860                     pwOutGlyphs[i] = newGlyph;
861             }
862             i++;
863         }
864     }
865
866     i = 0;
867     while (arabic_GSUB_features[i] != NULL)
868     {
869         apply_GSUB_feature(hdc, psa, psc->GSUB_Table, pwOutGlyphs, dirL, pcGlyphs, arabic_GSUB_features[i]);
870         i++;
871     }
872
873     HeapFree(GetProcessHeap(),0,context_shape);
874     HeapFree(GetProcessHeap(),0,context_type);
875 }