ntdll: Use list_empty() instead of list_count() == 0.
[wine/multimedia.git] / dlls / usp10 / indic.c
blob09de3b5c2be6e5c685d406698086fd9bf842cb67
1 /*
2 * Implementation of Indic Syllables for the Uniscribe Script Processor
4 * Copyright 2011 CodeWeavers, Aric Stewart
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.
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.
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
21 #include "config.h"
22 #include <stdarg.h>
23 #include <stdio.h>
24 #include <stdlib.h>
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"
34 #include "wine/debug.h"
35 #include "usp10_internal.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(uniscribe);
39 static void debug_output_string(LPCWSTR str, int cChar, lexical_function f)
41 int i;
42 if (TRACE_ON(uniscribe))
44 for (i = 0; i < cChar; i++)
46 switch (f(str[i]))
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;
67 TRACE("\n");
71 static inline BOOL is_matra( int type )
73 return (type == lex_Matra_above || type == lex_Matra_below ||
74 type == lex_Matra_pre || type == lex_Matra_post);
77 static inline BOOL is_joiner( int type )
79 return (type == lex_ZWJ || type == lex_ZWNJ);
82 static INT consonant_header(LPCWSTR input, INT cChar, INT start, INT next,
83 lexical_function lex)
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)
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;
97 else if (is_joiner( lex(input[next]) ) && lex(input[next+1])==lex_Halant)
99 next+=2;
100 if ((next < cChar) && is_consonant( lex(input[next]) ))
101 return next;
103 return -1;
106 static INT parse_consonant_syllable(LPCWSTR input, INT cChar, INT start,
107 INT *main, INT next, lexical_function lex)
109 int check;
110 int headers = 0;
113 check = consonant_header(input,cChar,start,next,lex);
114 if (check != -1)
116 next = check;
117 headers++;
119 } while (check != -1);
120 if (headers || is_consonant( lex(input[next]) ))
122 *main = next;
123 next++;
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++;
132 if ((next < cChar) && lex(input[next]) == lex_Halant)
134 next++;
135 if((next < cChar) && is_joiner( lex(input[next]) ))
136 next++;
138 else if (next < cChar)
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++;
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;
154 static INT parse_vowel_syllable(LPCWSTR input, INT cChar, INT start,
155 INT next, lexical_function lex)
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;
166 if (is_matra( lex(input[next]) ))
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++;
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;
183 static INT Indic_process_next_syllable( LPCWSTR input, INT cChar, INT start, INT* main, INT next, lexical_function lex )
185 if (lex(input[next])==lex_Vowel)
187 *main = next;
188 return parse_vowel_syllable(input, cChar, start, next+1, lex);
190 else if ((cChar > next+3) && lex(input[next]) == lex_Ra && lex(input[next+1]) == lex_Halant && lex(input[next+2]) == lex_Vowel)
192 *main = next+2;
193 return parse_vowel_syllable(input, cChar, start, next+3, lex);
196 else if (start == next && lex(input[next])==lex_NBSP)
198 *main = next;
199 return parse_vowel_syllable(input, cChar, start, next+1, lex);
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)
203 *main = next+2;
204 return parse_vowel_syllable(input, cChar, start, next+3, lex);
207 return parse_consonant_syllable(input, cChar, start, main, next, lex);
210 static BOOL Consonent_is_post_base_form(HDC hdc, SCRIPT_ANALYSIS *psa, ScriptCache* psc, LPWSTR pwChar, IndicSyllable *s, lexical_function lexical)
212 if (is_consonant(lexical(pwChar[s->base])) && s->base > s->start && lexical(pwChar[s->base-1]) == lex_Halant)
213 return (SHAPE_does_GSUB_feature_apply_to_chars(hdc, psa, psc, &pwChar[s->base-1], 1, 2, "pstf") > 0);
214 return FALSE;
217 static BOOL Consonent_is_below_base_form(HDC hdc, SCRIPT_ANALYSIS *psa, ScriptCache* psc, LPWSTR pwChar, IndicSyllable *s, lexical_function lexical)
219 if (is_consonant(lexical(pwChar[s->base])) && s->base > s->start && lexical(pwChar[s->base-1]) == lex_Halant)
220 return (SHAPE_does_GSUB_feature_apply_to_chars(hdc, psa, psc, &pwChar[s->base-1], 1, 2, "blwf") > 0);
221 return FALSE;
224 static BOOL Consonent_is_pre_base_form(HDC hdc, SCRIPT_ANALYSIS *psa, ScriptCache* psc, LPWSTR pwChar, IndicSyllable *s, lexical_function lexical)
226 if (is_consonant(lexical(pwChar[s->base])) && s->base > s->start && lexical(pwChar[s->base-1]) == lex_Halant)
227 return (SHAPE_does_GSUB_feature_apply_to_chars(hdc, psa, psc, &pwChar[s->base-1], 1, 2, "pref") > 0);
228 return FALSE;
231 static BOOL Consonent_is_ralf(HDC hdc, SCRIPT_ANALYSIS *psa, ScriptCache* psc, LPWSTR pwChar, IndicSyllable *s, lexical_function lexical)
233 if ((lexical(pwChar[s->start])==lex_Ra) && s->end > s->start && lexical(pwChar[s->start+1]) == lex_Halant)
234 return (SHAPE_does_GSUB_feature_apply_to_chars(hdc, psa, psc, &pwChar[s->start], 1, 2, "rphf") > 0);
235 return FALSE;
238 static int FindBaseConsonant(HDC hdc, SCRIPT_ANALYSIS *psa, ScriptCache* psc, LPWSTR input, IndicSyllable *s, lexical_function lex)
240 int i;
242 /* remove ralf from consideration */
243 if (Consonent_is_ralf(hdc, psa, psc, input, s, lex))
245 s->ralf = s->start;
246 s->start+=2;
249 /* try to find a base consonant */
250 if (!is_consonant( lex(input[s->base]) ))
252 for (i = s->end; i >= s->start; i--)
253 if (is_consonant( lex(input[i]) ))
255 s->base = i;
256 break;
260 while (Consonent_is_below_base_form(hdc, psa, psc, input, s, lex) || Consonent_is_post_base_form(hdc, psa, psc, input, s, lex) || Consonent_is_pre_base_form(hdc, psa, psc, input, s, lex))
262 for (i = s->base-1; i >= s->start; i--)
263 if (is_consonant( lex(input[i]) ))
265 s->base = i;
266 break;
270 if (s->ralf >= 0)
271 s->start = s->ralf;
273 return s->base;
276 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)
278 int index = 0;
279 int next = 0;
280 int center = 0;
282 *syllable_count = 0;
284 if (!lex || ! reorder_f)
286 ERR("Failure to have required functions\n");
287 return;
290 debug_output_string(input, cChar, lex);
291 while (next != -1)
293 while((next < cChar) && lex(input[next]) == lex_Generic)
294 next++;
295 index = next;
296 next = Indic_process_next_syllable(input, cChar, 0, &center, index, lex);
297 if (next != -1)
299 if (*syllable_count)
300 *syllables = HeapReAlloc(GetProcessHeap(),0,*syllables, sizeof(IndicSyllable)*(*syllable_count+1));
301 else
302 *syllables = HeapAlloc(GetProcessHeap(),0,sizeof(IndicSyllable));
303 (*syllables)[*syllable_count].start = index;
304 (*syllables)[*syllable_count].base = center;
305 (*syllables)[*syllable_count].ralf = -1;
306 (*syllables)[*syllable_count].end = next-1;
307 FindBaseConsonant(hdc, psa, psc, input, &(*syllables)[*syllable_count], lex);
308 reorder_f(input, &(*syllables)[*syllable_count], lex);
309 index = next;
310 *syllable_count = (*syllable_count)+1;
312 else if (index < cChar)
314 int i;
315 TRACE("Processing failed at %i\n",index);
316 for (i = index; i < cChar; i++)
317 if (lex(input[i])==lex_Generic)
319 TRACE("Restart processing at %i\n",i);
320 next = i;
321 index = i;
322 break;
326 TRACE("Processed %i of %i characters into %i syllables\n",index,cChar,*syllable_count);