usp10: Correct indic vowel marker name.
[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_consonant( int type )
72 {
73     return (type == lex_Ra || type == lex_Consonant);
74 }
75
76 static inline BOOL is_matra( int type )
77 {
78     return (type == lex_Matra_above || type == lex_Matra_below ||
79             type == lex_Matra_pre || type == lex_Matra_post);
80 }
81
82 static inline BOOL is_joiner( int type )
83 {
84     return (type == lex_ZWJ || type == lex_ZWNJ);
85 }
86
87 static INT consonant_header(LPCWSTR input, INT cChar, INT start, INT next,
88                             lexical_function lex)
89 {
90     if (!is_consonant( lex(input[next]) )) return -1;
91     next++;
92     if ((next < cChar) && lex(input[next]) == lex_Nukta)
93             next++;
94     if (lex(input[next])==lex_Halant)
95     {
96         next++;
97         if((next < cChar) && is_joiner( lex(input[next]) ))
98             next++;
99         if ((next < cChar) && is_consonant( lex(input[next]) ))
100             return next;
101     }
102     else if (is_joiner( lex(input[next]) ) && lex(input[next+1])==lex_Halant)
103     {
104         next+=2;
105         if ((next < cChar) && is_consonant( lex(input[next]) ))
106             return next;
107     }
108     return -1;
109 }
110
111 static INT parse_consonant_syllable(LPCWSTR input, INT cChar, INT start,
112                                     INT *main, INT next, lexical_function lex)
113 {
114     int check;
115     int headers = 0;
116     do
117     {
118         check = consonant_header(input,cChar,start,next,lex);
119         if (check != -1)
120         {
121             next = check;
122             headers++;
123         }
124     } while (check != -1);
125     if (headers || is_consonant( lex(input[next]) ))
126     {
127         *main = next;
128         next++;
129     }
130     else
131         return -1;
132     if ((next < cChar) && lex(input[next]) == lex_Nukta)
133             next++;
134     if ((next < cChar) && lex(input[next]) == lex_Anudatta)
135             next++;
136
137     if ((next < cChar) && lex(input[next]) == lex_Halant)
138     {
139         next++;
140         if((next < cChar) && is_joiner( lex(input[next]) ))
141             next++;
142     }
143     else if (next < cChar)
144     {
145         while((next < cChar) && is_matra( lex(input[next]) ))
146             next++;
147         if ((next < cChar) && lex(input[next]) == lex_Nukta)
148             next++;
149         if ((next < cChar) && lex(input[next]) == lex_Halant)
150             next++;
151     }
152     if ((next < cChar) && lex(input[next]) == lex_Modifier)
153             next++;
154     if ((next < cChar) && lex(input[next]) == lex_Vedic)
155             next++;
156     return next;
157 }
158
159 static INT parse_vowel_syllable(LPCWSTR input, INT cChar, INT start,
160                                     INT next, lexical_function lex)
161 {
162     if ((next < cChar) && lex(input[next]) == lex_Nukta)
163         next++;
164     if ((next < cChar) && is_joiner( lex(input[next]) ) && lex(input[next+1])==lex_Halant && is_consonant( lex(input[next+2]) ))
165         next+=3;
166     else if ((next < cChar) && lex(input[next])==lex_Halant && is_consonant( lex(input[next+1]) ))
167         next+=2;
168     else if ((next < cChar) && lex(input[next])==lex_ZWJ && is_consonant( lex(input[next+1]) ))
169         next+=2;
170
171     if (is_matra( lex(input[next]) ))
172     {
173         while((next < cChar) && is_matra( lex(input[next]) ))
174             next++;
175         if ((next < cChar) && lex(input[next]) == lex_Nukta)
176             next++;
177         if ((next < cChar) && lex(input[next]) == lex_Halant)
178             next++;
179     }
180
181     if ((next < cChar) && lex(input[next]) == lex_Modifier)
182         next++;
183     if ((next < cChar) && lex(input[next]) == lex_Vedic)
184         next++;
185     return next;
186 }
187
188 static INT Indic_process_next_syllable( LPCWSTR input, INT cChar, INT start, INT* main, INT next, lexical_function lex )
189 {
190     if (lex(input[next])==lex_Vowel)
191     {
192         *main = next;
193         return parse_vowel_syllable(input, cChar, start, next+1, lex);
194     }
195     else if ((cChar > next+3) && lex(input[next]) == lex_Ra && lex(input[next+1]) == lex_Halant && lex(input[next+2]) == lex_Vowel)
196     {
197         *main = next+2;
198         return parse_vowel_syllable(input, cChar, start, next+3, lex);
199     }
200
201     else if (start == next && lex(input[next])==lex_NBSP)
202     {
203         *main = next;
204         return parse_vowel_syllable(input, cChar, start, next+1, lex);
205     }
206     else if (start == next && (cChar > next+3) && lex(input[next]) == lex_Ra && lex(input[next+1]) == lex_Halant && lex(input[next+2]) == lex_NBSP)
207     {
208         *main = next+2;
209         return parse_vowel_syllable(input, cChar, start, next+3, lex);
210     }
211
212     return parse_consonant_syllable(input, cChar, start, main, next, lex);
213 }
214
215 void Indic_ReorderCharacters( LPWSTR input, int cChar, lexical_function lex, reorder_function reorder_f)
216 {
217     int index = 0;
218     int next = 0;
219     int center = 0;
220
221     if (!lex || ! reorder_f)
222     {
223         ERR("Failure to have required functions\n");
224         return;
225     }
226
227     debug_output_string(input, cChar, lex);
228     while (next != -1)
229     {
230         while((next < cChar) && lex(input[next]) == lex_Generic)
231             next++;
232         index = next;
233         next = Indic_process_next_syllable(input, cChar, 0, &center, index, lex);
234         if (next != -1)
235         {
236             reorder_f(input, index, center, next-1, lex);
237             index = next;
238         }
239         else if (index < cChar)
240         {
241             int i;
242             TRACE("Processing failed at %i\n",index);
243             for (i = index; i < cChar; i++)
244                 if (lex(input[i])==lex_Generic)
245                 {
246                     TRACE("Restart processing at %i\n",i);
247                     next = i;
248                     index = i;
249                     break;
250                 }
251         }
252     }
253     TRACE("Processed %i of %i characters\n",index,cChar);
254 }
255
256 int Indic_FindBaseConsonant(LPWSTR input, INT start, INT main, INT end, lexical_function lex)
257 {
258     int i;
259     /* try to find a base consonant */
260     if (!is_consonant( lex(input[main]) ))
261     {
262         for (i = end; i >= start; i--)
263             if (is_consonant( lex(input[i]) ))
264             {
265                 main = i;
266                 break;
267             }
268     }
269     return main;
270 }