shdocvw: Add ITaskbarList2 Interface.
[wine] / dlls / usp10 / indic.c
1 /*
2  * Implementation of Indic Syllables for the Uniscribe Script Processor
3  *
4  * Copyright 2011 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 "config.h"
22 #include <stdarg.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winuser.h"
29 #include "wingdi.h"
30 #include "winnls.h"
31 #include "usp10.h"
32 #include "winternl.h"
33
34 #include "wine/debug.h"
35 #include "usp10_internal.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(uniscribe);
38
39 static void debug_output_string(LPCWSTR str, int cChar, lexical_function f)
40 {
41     int i;
42     if (TRACE_ON(uniscribe))
43     {
44         for (i = 0; i < cChar; i++)
45         {
46             switch (f(str[i]))
47             {
48                 case lex_Consonant: TRACE("C"); break;
49                 case lex_Ra: TRACE("Ra"); break;
50                 case lex_Vowel: TRACE("V"); break;
51                 case lex_Nukta: TRACE("N"); break;
52                 case lex_Halant: TRACE("H"); break;
53                 case lex_ZWNJ: TRACE("Zwnj"); break;
54                 case lex_ZWJ: TRACE("Zwj"); break;
55                 case lex_Matra_post: TRACE("Mp");break;
56                 case lex_Matra_above: TRACE("Ma");break;
57                 case lex_Matra_below: TRACE("Mb");break;
58                 case lex_Matra_pre: TRACE("Mm");break;
59                 case lex_Modifier: TRACE("Sm"); break;
60                 case lex_Vedic: TRACE("Vd"); break;
61                 case lex_Anudatta: TRACE("A"); break;
62                 case lex_Composed_Vowel: TRACE("t"); break;
63                 default:
64                     TRACE("X"); break;
65             }
66         }
67         TRACE("\n");
68     }
69 }
70
71 static inline BOOL is_matra( int type )
72 {
73     return (type == lex_Matra_above || type == lex_Matra_below ||
74             type == lex_Matra_pre || type == lex_Matra_post);
75 }
76
77 static inline BOOL is_joiner( int type )
78 {
79     return (type == lex_ZWJ || type == lex_ZWNJ);
80 }
81
82 static INT consonant_header(LPCWSTR input, INT cChar, INT start, INT next,
83                             lexical_function lex)
84 {
85     if (!is_consonant( lex(input[next]) )) return -1;
86     next++;
87     if ((next < cChar) && lex(input[next]) == lex_Nukta)
88             next++;
89     if (lex(input[next])==lex_Halant)
90     {
91         next++;
92         if((next < cChar) && is_joiner( lex(input[next]) ))
93             next++;
94         if ((next < cChar) && is_consonant( lex(input[next]) ))
95             return next;
96     }
97     else if (is_joiner( lex(input[next]) ) && lex(input[next+1])==lex_Halant)
98     {
99         next+=2;
100         if ((next < cChar) && is_consonant( lex(input[next]) ))
101             return next;
102     }
103     return -1;
104 }
105
106 static INT parse_consonant_syllable(LPCWSTR input, INT cChar, INT start,
107                                     INT *main, INT next, lexical_function lex)
108 {
109     int check;
110     int headers = 0;
111     do
112     {
113         check = consonant_header(input,cChar,start,next,lex);
114         if (check != -1)
115         {
116             next = check;
117             headers++;
118         }
119     } while (check != -1);
120     if (headers || is_consonant( lex(input[next]) ))
121     {
122         *main = next;
123         next++;
124     }
125     else
126         return -1;
127     if ((next < cChar) && lex(input[next]) == lex_Nukta)
128             next++;
129     if ((next < cChar) && lex(input[next]) == lex_Anudatta)
130             next++;
131
132     if ((next < cChar) && lex(input[next]) == lex_Halant)
133     {
134         next++;
135         if((next < cChar) && is_joiner( lex(input[next]) ))
136             next++;
137     }
138     else if (next < cChar)
139     {
140         while((next < cChar) && is_matra( lex(input[next]) ))
141             next++;
142         if ((next < cChar) && lex(input[next]) == lex_Nukta)
143             next++;
144         if ((next < cChar) && lex(input[next]) == lex_Halant)
145             next++;
146     }
147     if ((next < cChar) && lex(input[next]) == lex_Modifier)
148             next++;
149     if ((next < cChar) && lex(input[next]) == lex_Vedic)
150             next++;
151     return next;
152 }
153
154 static INT parse_vowel_syllable(LPCWSTR input, INT cChar, INT start,
155                                     INT next, lexical_function lex)
156 {
157     if ((next < cChar) && lex(input[next]) == lex_Nukta)
158         next++;
159     if ((next < cChar) && is_joiner( lex(input[next]) ) && lex(input[next+1])==lex_Halant && is_consonant( lex(input[next+2]) ))
160         next+=3;
161     else if ((next < cChar) && lex(input[next])==lex_Halant && is_consonant( lex(input[next+1]) ))
162         next+=2;
163     else if ((next < cChar) && lex(input[next])==lex_ZWJ && is_consonant( lex(input[next+1]) ))
164         next+=2;
165
166     if (is_matra( lex(input[next]) ))
167     {
168         while((next < cChar) && is_matra( lex(input[next]) ))
169             next++;
170         if ((next < cChar) && lex(input[next]) == lex_Nukta)
171             next++;
172         if ((next < cChar) && lex(input[next]) == lex_Halant)
173             next++;
174     }
175
176     if ((next < cChar) && lex(input[next]) == lex_Modifier)
177         next++;
178     if ((next < cChar) && lex(input[next]) == lex_Vedic)
179         next++;
180     return next;
181 }
182
183 static INT Indic_process_next_syllable( LPCWSTR input, INT cChar, INT start, INT* main, INT next, lexical_function lex )
184 {
185     if (lex(input[next])==lex_Vowel)
186     {
187         *main = next;
188         return parse_vowel_syllable(input, cChar, start, next+1, lex);
189     }
190     else if ((cChar > next+3) && lex(input[next]) == lex_Ra && lex(input[next+1]) == lex_Halant && lex(input[next+2]) == lex_Vowel)
191     {
192         *main = next+2;
193         return parse_vowel_syllable(input, cChar, start, next+3, lex);
194     }
195
196     else if (start == next && lex(input[next])==lex_NBSP)
197     {
198         *main = next;
199         return parse_vowel_syllable(input, cChar, start, next+1, lex);
200     }
201     else if (start == next && (cChar > next+3) && lex(input[next]) == lex_Ra && lex(input[next+1]) == lex_Halant && lex(input[next+2]) == lex_NBSP)
202     {
203         *main = next+2;
204         return parse_vowel_syllable(input, cChar, start, next+3, lex);
205     }
206
207     return parse_consonant_syllable(input, cChar, start, main, next, lex);
208 }
209
210 static BOOL Consonent_is_post_base_form(HDC hdc, SCRIPT_ANALYSIS *psa, ScriptCache* psc, LPWSTR pwChar, IndicSyllable *s, lexical_function lexical, BOOL modern)
211 {
212     if (is_consonant(lexical(pwChar[s->base])) && s->base > s->start && lexical(pwChar[s->base-1]) == lex_Halant)
213     {
214         if (modern)
215             return (SHAPE_does_GSUB_feature_apply_to_chars(hdc, psa, psc, &pwChar[s->base-1], 1, 2, "pstf") > 0);
216         else
217         {
218             WCHAR cc[2];
219             cc[0] = pwChar[s->base];
220             cc[1] = pwChar[s->base-1];
221             return (SHAPE_does_GSUB_feature_apply_to_chars(hdc, psa, psc, cc, 1, 2, "pstf") > 0);
222         }
223     }
224     return FALSE;
225 }
226
227 static BOOL Consonent_is_below_base_form(HDC hdc, SCRIPT_ANALYSIS *psa, ScriptCache* psc, LPWSTR pwChar, IndicSyllable *s, lexical_function lexical, BOOL modern)
228 {
229     if (is_consonant(lexical(pwChar[s->base])) && s->base > s->start && lexical(pwChar[s->base-1]) == lex_Halant)
230     {
231         if (modern)
232             return (SHAPE_does_GSUB_feature_apply_to_chars(hdc, psa, psc, &pwChar[s->base-1], 1, 2, "blwf") > 0);
233         else
234         {
235             WCHAR cc[2];
236             cc[0] = pwChar[s->base];
237             cc[1] = pwChar[s->base-1];
238             return (SHAPE_does_GSUB_feature_apply_to_chars(hdc, psa, psc, cc, 1, 2, "blwf") > 0);
239         }
240     }
241     return FALSE;
242 }
243
244 static BOOL Consonent_is_pre_base_form(HDC hdc, SCRIPT_ANALYSIS *psa, ScriptCache* psc, LPWSTR pwChar, IndicSyllable *s, lexical_function lexical, BOOL modern)
245 {
246     if (is_consonant(lexical(pwChar[s->base])) && s->base > s->start && lexical(pwChar[s->base-1]) == lex_Halant)
247     {
248         if (modern)
249             return (SHAPE_does_GSUB_feature_apply_to_chars(hdc, psa, psc, &pwChar[s->base-1], 1, 2, "pref") > 0);
250         else
251         {
252             WCHAR cc[2];
253             cc[0] = pwChar[s->base];
254             cc[1] = pwChar[s->base-1];
255             return (SHAPE_does_GSUB_feature_apply_to_chars(hdc, psa, psc, cc, 1, 2, "pref") > 0);
256         }
257     }
258     return FALSE;
259 }
260
261 static BOOL Consonent_is_ralf(HDC hdc, SCRIPT_ANALYSIS *psa, ScriptCache* psc, LPWSTR pwChar, IndicSyllable *s, lexical_function lexical)
262 {
263     if ((lexical(pwChar[s->start])==lex_Ra) && s->end > s->start && lexical(pwChar[s->start+1]) == lex_Halant)
264         return (SHAPE_does_GSUB_feature_apply_to_chars(hdc, psa, psc, &pwChar[s->start], 1, 2, "rphf") > 0);
265     return FALSE;
266 }
267
268 static int FindBaseConsonant(HDC hdc, SCRIPT_ANALYSIS *psa, ScriptCache* psc, LPWSTR input, IndicSyllable *s, lexical_function lex, BOOL modern)
269 {
270     int i;
271     BOOL blwf = FALSE;
272     BOOL pref = FALSE;
273
274     /* remove ralf from consideration */
275     if (Consonent_is_ralf(hdc, psa, psc, input, s, lex))
276     {
277         s->ralf = s->start;
278         s->start+=2;
279     }
280
281     /* try to find a base consonant */
282     if (!is_consonant( lex(input[s->base]) ))
283     {
284         for (i = s->end; i >= s->start; i--)
285             if (is_consonant( lex(input[i]) ))
286             {
287                 s->base = i;
288                 break;
289             }
290     }
291
292     while ((blwf = Consonent_is_below_base_form(hdc, psa, psc, input, s, lex, modern)) || Consonent_is_post_base_form(hdc, psa, psc, input, s, lex, modern) || (pref = Consonent_is_pre_base_form(hdc, psa, psc, input, s, lex, modern)))
293     {
294         if (blwf && s->blwf == -1)
295             s->blwf = s->base - 1;
296         if (pref && s->pref == -1)
297             s->pref = s->base - 1;
298
299         for (i = s->base-1; i >= s->start; i--)
300             if (is_consonant( lex(input[i]) ))
301             {
302                 s->base = i;
303                 break;
304             }
305     }
306
307     if (s->ralf >= 0)
308         s->start = s->ralf;
309
310     if (s->ralf == s->base)
311         s->ralf = -1;
312
313     return s->base;
314 }
315
316 void Indic_ReorderCharacters( HDC hdc, SCRIPT_ANALYSIS *psa, ScriptCache* psc, LPWSTR input, int cChar, IndicSyllable **syllables, int *syllable_count, lexical_function lex, reorder_function reorder_f, BOOL modern)
317 {
318     int index = 0;
319     int next = 0;
320     int center = 0;
321
322     *syllable_count = 0;
323
324     if (!lex || ! reorder_f)
325     {
326         ERR("Failure to have required functions\n");
327         return;
328     }
329
330     debug_output_string(input, cChar, lex);
331     while (next != -1)
332     {
333         while((next < cChar) && lex(input[next]) == lex_Generic)
334             next++;
335         index = next;
336         next = Indic_process_next_syllable(input, cChar, 0, &center, index, lex);
337         if (next != -1)
338         {
339             if (*syllable_count)
340                 *syllables = HeapReAlloc(GetProcessHeap(),0,*syllables, sizeof(IndicSyllable)*(*syllable_count+1));
341             else
342                 *syllables = HeapAlloc(GetProcessHeap(),0,sizeof(IndicSyllable));
343             (*syllables)[*syllable_count].start = index;
344             (*syllables)[*syllable_count].base = center;
345             (*syllables)[*syllable_count].ralf = -1;
346             (*syllables)[*syllable_count].blwf = -1;
347             (*syllables)[*syllable_count].pref = -1;
348             (*syllables)[*syllable_count].end = next-1;
349             FindBaseConsonant(hdc, psa, psc, input, &(*syllables)[*syllable_count], lex, modern);
350             reorder_f(input, &(*syllables)[*syllable_count], lex);
351             index = next;
352             *syllable_count = (*syllable_count)+1;
353         }
354         else if (index < cChar)
355         {
356             TRACE("Processing failed at %i\n",index);
357             next = ++index;
358         }
359     }
360     TRACE("Processed %i of %i characters into %i syllables\n",index,cChar,*syllable_count);
361 }