wineoss.drv: Avoid a potential NULL pointer dereference in a TRACE.
[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     Xl,
55     Xr,
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
74 typedef struct {
75     DWORD version;
76     WORD ScriptList;
77     WORD FeatureList;
78     WORD LookupList;
79 } GSUB_Header;
80
81 typedef struct {
82     CHAR ScriptTag[4];
83     WORD Script;
84 } GSUB_ScriptRecord;
85
86 typedef struct {
87     WORD ScriptCount;
88     GSUB_ScriptRecord ScriptRecord[1];
89 } GSUB_ScriptList;
90
91 typedef struct {
92     CHAR LangSysTag[4];
93     WORD LangSys;
94 } GSUB_LangSysRecord;
95
96 typedef struct {
97     WORD DefaultLangSys;
98     WORD LangSysCount;
99     GSUB_LangSysRecord LangSysRecord[1];
100 } GSUB_Script;
101
102 typedef struct {
103     WORD LookupOrder; /* Reserved */
104     WORD ReqFeatureIndex;
105     WORD FeatureCount;
106     WORD FeatureIndex[1];
107 } GSUB_LangSys;
108
109 typedef struct {
110     CHAR FeatureTag[4];
111     WORD Feature;
112 } GSUB_FeatureRecord;
113
114 typedef struct {
115     WORD FeatureCount;
116     GSUB_FeatureRecord FeatureRecord[1];
117 } GSUB_FeatureList;
118
119 typedef struct {
120     WORD FeatureParams; /* Reserved */
121     WORD LookupCount;
122     WORD LookupListIndex[1];
123 } GSUB_Feature;
124
125 typedef struct {
126     WORD LookupCount;
127     WORD Lookup[1];
128 } GSUB_LookupList;
129
130 typedef struct {
131     WORD LookupType;
132     WORD LookupFlag;
133     WORD SubTableCount;
134     WORD SubTable[1];
135 } GSUB_LookupTable;
136
137 typedef struct {
138     WORD CoverageFormat;
139     WORD GlyphCount;
140     WORD GlyphArray[1];
141 } GSUB_CoverageFormat1;
142
143 typedef struct {
144     WORD Start;
145     WORD End;
146     WORD StartCoverageIndex;
147 } GSUB_RangeRecord;
148
149 typedef struct {
150     WORD CoverageFormat;
151     WORD RangeCount;
152     GSUB_RangeRecord RangeRecord[1];
153 } GSUB_CoverageFormat2;
154
155 typedef struct {
156     WORD SubstFormat; /* = 1 */
157     WORD Coverage;
158     WORD DeltaGlyphID;
159 } GSUB_SingleSubstFormat1;
160
161 typedef struct {
162     WORD SubstFormat; /* = 2 */
163     WORD Coverage;
164     WORD GlyphCount;
165     WORD Substitute[1];
166 }GSUB_SingleSubstFormat2;
167
168 /* the orders of joined_forms and contextual_features need to line up */
169 static const char* contextual_features[] =
170 {
171     "isol",
172     "fina",
173     "init",
174     "medi"
175 };
176
177 static INT GSUB_is_glyph_covered(LPCVOID table , UINT glyph)
178 {
179     const GSUB_CoverageFormat1* cf1;
180
181     cf1 = table;
182
183     if (GET_BE_WORD(cf1->CoverageFormat) == 1)
184     {
185         int count = GET_BE_WORD(cf1->GlyphCount);
186         int i;
187         TRACE("Coverage Format 1, %i glyphs\n",count);
188         for (i = 0; i < count; i++)
189             if (glyph == GET_BE_WORD(cf1->GlyphArray[i]))
190                 return i;
191         return -1;
192     }
193     else if (GET_BE_WORD(cf1->CoverageFormat) == 2)
194     {
195         const GSUB_CoverageFormat2* cf2;
196         int i;
197         int count;
198         cf2 = (const GSUB_CoverageFormat2*)cf1;
199
200         count = GET_BE_WORD(cf2->RangeCount);
201         TRACE("Coverage Format 2, %i ranges\n",count);
202         for (i = 0; i < count; i++)
203         {
204             if (glyph < GET_BE_WORD(cf2->RangeRecord[i].Start))
205                 return -1;
206             if ((glyph >= GET_BE_WORD(cf2->RangeRecord[i].Start)) &&
207                 (glyph <= GET_BE_WORD(cf2->RangeRecord[i].End)))
208             {
209                 return (GET_BE_WORD(cf2->RangeRecord[i].StartCoverageIndex) +
210                     glyph - GET_BE_WORD(cf2->RangeRecord[i].Start));
211             }
212         }
213         return -1;
214     }
215     else
216         ERR("Unknown CoverageFormat %i\n",GET_BE_WORD(cf1->CoverageFormat));
217
218     return -1;
219 }
220
221 static const GSUB_Script* GSUB_get_script_table( const GSUB_Header* header, const char* tag)
222 {
223     const GSUB_ScriptList *script;
224     const GSUB_Script *deflt = NULL;
225     int i;
226     script = (const GSUB_ScriptList*)((const BYTE*)header + GET_BE_WORD(header->ScriptList));
227
228     TRACE("%i scripts in this font\n",GET_BE_WORD(script->ScriptCount));
229     for (i = 0; i < GET_BE_WORD(script->ScriptCount); i++)
230     {
231         const GSUB_Script *scr;
232         int offset;
233
234         offset = GET_BE_WORD(script->ScriptRecord[i].Script);
235         scr = (const GSUB_Script*)((const BYTE*)script + offset);
236
237         if (strncmp(script->ScriptRecord[i].ScriptTag, tag,4)==0)
238             return scr;
239         if (strncmp(script->ScriptRecord[i].ScriptTag, "dflt",4)==0)
240             deflt = scr;
241     }
242     return deflt;
243 }
244
245 static const GSUB_LangSys* GSUB_get_lang_table( const GSUB_Script* script, const char* tag)
246 {
247     int i;
248     int offset;
249     const GSUB_LangSys *Lang;
250
251     TRACE("Deflang %x, LangCount %i\n",GET_BE_WORD(script->DefaultLangSys), GET_BE_WORD(script->LangSysCount));
252
253     for (i = 0; i < GET_BE_WORD(script->LangSysCount) ; i++)
254     {
255         offset = GET_BE_WORD(script->LangSysRecord[i].LangSys);
256         Lang = (const GSUB_LangSys*)((const BYTE*)script + offset);
257
258         if ( strncmp(script->LangSysRecord[i].LangSysTag,tag,4)==0)
259             return Lang;
260     }
261     offset = GET_BE_WORD(script->DefaultLangSys);
262     if (offset)
263     {
264         Lang = (const GSUB_LangSys*)((const BYTE*)script + offset);
265         return Lang;
266     }
267     return NULL;
268 }
269
270 static const GSUB_Feature * GSUB_get_feature(const GSUB_Header *header, const GSUB_LangSys *lang, const char* tag)
271 {
272     int i;
273     const GSUB_FeatureList *feature;
274     feature = (const GSUB_FeatureList*)((const BYTE*)header + GET_BE_WORD(header->FeatureList));
275
276     TRACE("%i features\n",GET_BE_WORD(lang->FeatureCount));
277     for (i = 0; i < GET_BE_WORD(lang->FeatureCount); i++)
278     {
279         int index = GET_BE_WORD(lang->FeatureIndex[i]);
280         if (strncmp(feature->FeatureRecord[index].FeatureTag,tag,4)==0)
281         {
282             const GSUB_Feature *feat;
283             feat = (const GSUB_Feature*)((const BYTE*)feature + GET_BE_WORD(feature->FeatureRecord[index].Feature));
284             return feat;
285         }
286     }
287     return NULL;
288 }
289
290 static UINT GSUB_apply_feature(const GSUB_Header * header, const GSUB_Feature* feature, UINT glyph)
291 {
292     int i;
293     int offset;
294     const GSUB_LookupList *lookup;
295     lookup = (const GSUB_LookupList*)((const BYTE*)header + GET_BE_WORD(header->LookupList));
296
297     TRACE("%i lookups\n", GET_BE_WORD(feature->LookupCount));
298     for (i = 0; i < GET_BE_WORD(feature->LookupCount); i++)
299     {
300         const GSUB_LookupTable *look;
301         offset = GET_BE_WORD(lookup->Lookup[GET_BE_WORD(feature->LookupListIndex[i])]);
302         look = (const GSUB_LookupTable*)((const BYTE*)lookup + offset);
303         TRACE("type %i, flag %x, subtables %i\n",GET_BE_WORD(look->LookupType),GET_BE_WORD(look->LookupFlag),GET_BE_WORD(look->SubTableCount));
304         if (GET_BE_WORD(look->LookupType) != 1)
305             FIXME("We only handle SubType 1 (%i)\n",GET_BE_WORD(look->LookupType));
306         else
307         {
308             int j;
309
310             for (j = 0; j < GET_BE_WORD(look->SubTableCount); j++)
311             {
312                 const GSUB_SingleSubstFormat1 *ssf1;
313                 offset = GET_BE_WORD(look->SubTable[j]);
314                 ssf1 = (const GSUB_SingleSubstFormat1*)((const BYTE*)look+offset);
315                 if (GET_BE_WORD(ssf1->SubstFormat) == 1)
316                 {
317                     int offset = GET_BE_WORD(ssf1->Coverage);
318                     TRACE("  subtype 1, delta %i\n", GET_BE_WORD(ssf1->DeltaGlyphID));
319                     if (GSUB_is_glyph_covered((const BYTE*)ssf1+offset, glyph) != -1)
320                     {
321                         TRACE("  Glyph 0x%x ->",glyph);
322                         glyph += GET_BE_WORD(ssf1->DeltaGlyphID);
323                         TRACE(" 0x%x\n",glyph);
324                     }
325                 }
326                 else
327                 {
328                     const GSUB_SingleSubstFormat2 *ssf2;
329                     INT index;
330                     INT offset;
331
332                     ssf2 = (const GSUB_SingleSubstFormat2 *)ssf1;
333                     offset = GET_BE_WORD(ssf1->Coverage);
334                     TRACE("  subtype 2,  glyph count %i\n", GET_BE_WORD(ssf2->GlyphCount));
335                     index = GSUB_is_glyph_covered((const BYTE*)ssf2+offset, glyph);
336                     TRACE("  Coverage index %i\n",index);
337                     if (index != -1)
338                     {
339                         TRACE("    Glyph is 0x%x ->",glyph);
340                         glyph = GET_BE_WORD(ssf2->Substitute[index]);
341                         TRACE("0x%x\n",glyph);
342                     }
343                 }
344             }
345         }
346     }
347     return glyph;
348 }
349
350 static const char* get_opentype_script(HDC hdc)
351 {
352     /*
353      * I am not sure if this is the correct way to generate our script tag
354      */
355     UINT charset = GetTextCharsetInfo(hdc, NULL, 0x0);
356
357     switch (charset)
358     {
359         case ANSI_CHARSET: return "latn";
360         case BALTIC_CHARSET: return "latn"; /* ?? */
361         case CHINESEBIG5_CHARSET: return "hani";
362         case EASTEUROPE_CHARSET: return "latn"; /* ?? */
363         case GB2312_CHARSET: return "hani";
364         case GREEK_CHARSET: return "grek";
365         case HANGUL_CHARSET: return "hang";
366         case RUSSIAN_CHARSET: return "cyrl";
367         case SHIFTJIS_CHARSET: return "kana";
368         case TURKISH_CHARSET: return "latn"; /* ?? */
369         case VIETNAMESE_CHARSET: return "latn";
370         case JOHAB_CHARSET: return "latn"; /* ?? */
371         case ARABIC_CHARSET: return "arab";
372         case HEBREW_CHARSET: return "hebr";
373         case THAI_CHARSET: return "thai";
374         default: return "latn";
375     }
376 }
377
378 static WORD get_GSUB_feature_glyph(HDC hdc, void* GSUB_Table, UINT glyph, const char* feat)
379 {
380     const GSUB_Header *header;
381     const GSUB_Script *script;
382     const GSUB_LangSys *language;
383     const GSUB_Feature *feature;
384
385     if (!GSUB_Table)
386         return glyph;
387
388     header = GSUB_Table;
389
390     script = GSUB_get_script_table(header, get_opentype_script(hdc));
391     if (!script)
392     {
393         TRACE("Script not found\n");
394         return glyph;
395     }
396     language = GSUB_get_lang_table(script, "xxxx"); /* Need to get Lang tag */
397     if (!language)
398     {
399         TRACE("Language not found\n");
400         return glyph;
401     }
402     feature  =  GSUB_get_feature(header, language, feat);
403     if (!feature)
404     {
405         TRACE("%s feature not found\n",feat);
406         return glyph;
407     }
408     return GSUB_apply_feature(header, feature, glyph);
409 }
410
411 static VOID *load_gsub_table(HDC hdc)
412 {
413     VOID* GSUB_Table = NULL;
414     int length = GetFontData(hdc, GSUB_TAG , 0, NULL, 0);
415     if (length != GDI_ERROR)
416     {
417         GSUB_Table = HeapAlloc(GetProcessHeap(),0,length);
418         GetFontData(hdc, GSUB_TAG , 0, GSUB_Table, length);
419         TRACE("Loaded GSUB table of %i bytes\n",length);
420     }
421     return GSUB_Table;
422 }
423
424 static CHAR neighbour_joining_type(int i, int delta, const CHAR* context_type, INT cchLen, SCRIPT_ANALYSIS *psa)
425 {
426     if (i + delta < 0)
427     {
428         if (psa->fLinkBefore)
429             return jtR;
430         else
431             return jtU;
432     }
433     if ( i+ delta >= cchLen)
434     {
435         if (psa->fLinkAfter)
436             return jtL;
437         else
438             return jtU;
439     }
440
441     i += delta;
442
443     if (context_type[i] == jtT)
444         return neighbour_joining_type(i,delta,context_type,cchLen,psa);
445     else
446         return context_type[i];
447 }
448
449 static inline BOOL right_join_causing(CHAR joining_type)
450 {
451     return (joining_type == jtR || joining_type == jtD || joining_type == jtC);
452 }
453
454 static inline BOOL left_join_causing(CHAR joining_type)
455 {
456     return (joining_type == jtL || joining_type == jtD || joining_type == jtC);
457 }
458
459 /* SHAPE_ShapeArabicGlyphs
460  */
461 void SHAPE_ShapeArabicGlyphs(HDC hdc, ScriptCache *psc, SCRIPT_ANALYSIS *psa, WCHAR* pwcChars, INT cChars, WORD* pwOutGlyphs, INT cMaxGlyphs)
462 {
463     CHAR *context_type;
464     INT *context_shape;
465     INT dirR, dirL;
466     int i;
467
468     if (psa->eScript != Script_Arabic)
469         return;
470
471     if (!psa->fLogicalOrder && psa->fRTL)
472     {
473         dirR = -1;
474         dirL = 1;
475     }
476     else
477     {
478         dirR = 1;
479         dirL = -1;
480     }
481
482     if (!psc->GSUB_Table)
483         psc->GSUB_Table = load_gsub_table(hdc);
484
485     context_type = HeapAlloc(GetProcessHeap(),0,cChars);
486     context_shape = HeapAlloc(GetProcessHeap(),0,sizeof(INT) * cChars);
487
488     for (i = 0; i < cChars; i++)
489         context_type[i] = wine_shaping_table[wine_shaping_table[pwcChars[i] >> 8] + (pwcChars[i] & 0xff)];
490
491     for (i = 0; i < cChars; i++)
492     {
493         if (context_type[i] == jtR && right_join_causing(neighbour_joining_type(i,dirR,context_type,cChars,psa)))
494             context_shape[i] = Xr;
495         else if (context_type[i] == jtL && left_join_causing(neighbour_joining_type(i,dirL,context_type,cChars,psa)))
496             context_shape[i] = Xl;
497         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)))
498             context_shape[i] = Xm;
499         else if (context_type[i] == jtD && right_join_causing(neighbour_joining_type(i,dirR,context_type,cChars,psa)))
500             context_shape[i] = Xr;
501         else if (context_type[i] == jtD && left_join_causing(neighbour_joining_type(i,dirL,context_type,cChars,psa)))
502             context_shape[i] = Xl;
503         else
504             context_shape[i] = Xn;
505     }
506
507     for (i = 0; i < cChars; i++)
508     {
509         WORD newGlyph = pwOutGlyphs[i];
510
511         if (psc->GSUB_Table)
512             newGlyph = get_GSUB_feature_glyph(hdc, psc->GSUB_Table, pwOutGlyphs[i], contextual_features[context_shape[i]]);
513         if (newGlyph == pwOutGlyphs[i] && pwcChars[i] >= FIRST_ARABIC_CHAR && pwcChars[i] <= LAST_ARABIC_CHAR)
514         {
515             /* fall back to presentation form B */
516             WCHAR context_char = wine_shaping_forms[pwcChars[i] - FIRST_ARABIC_CHAR][context_shape[i]];
517             if (context_char != pwcChars[i] && GetGlyphIndicesW(hdc, &context_char, 1, &newGlyph, 0) != GDI_ERROR && newGlyph != 0x0000)
518                 pwOutGlyphs[i] = newGlyph;
519         }
520         else if (newGlyph != pwOutGlyphs[i])
521             pwOutGlyphs[i] = newGlyph;
522     }
523
524     HeapFree(GetProcessHeap(),0,context_shape);
525     HeapFree(GetProcessHeap(),0,context_type);
526 }