]>
Commit | Line | Data |
---|---|---|
b75a7d8f | 1 | /* |
b75a7d8f | 2 | * |
374ca955 | 3 | * (C) Copyright IBM Corp. 1998-2004 - All Rights Reserved |
b75a7d8f A |
4 | * |
5 | */ | |
6 | ||
7 | #include "LETypes.h" | |
8 | #include "LEGlyphFilter.h" | |
9 | #include "OpenTypeTables.h" | |
374ca955 | 10 | #include "LEGlyphStorage.h" |
b75a7d8f A |
11 | #include "ThaiShaping.h" |
12 | ||
13 | U_NAMESPACE_BEGIN | |
14 | ||
15 | enum { | |
16 | CH_SPACE = 0x0020, | |
17 | CH_YAMAKKAN = 0x0E4E, | |
18 | CH_MAI_HANAKAT = 0x0E31, | |
19 | CH_SARA_AA = 0x0E32, | |
20 | CH_SARA_AM = 0x0E33, | |
21 | CH_SARA_UEE = 0x0E37, | |
22 | CH_MAITAIKHU = 0x0E47, | |
23 | CH_NIKHAHIT = 0x0E4D, | |
24 | CH_SARA_U = 0x0E38, | |
25 | CH_PHINTHU = 0x0E3A, | |
26 | CH_YO_YING = 0x0E0D, | |
27 | CH_THO_THAN = 0x0E10, | |
28 | CH_DOTTED_CIRCLE = 0x25CC | |
29 | }; | |
30 | ||
31 | le_uint8 ThaiShaping::getCharClass(LEUnicode ch) | |
32 | { | |
33 | le_uint8 charClass = NON; | |
34 | ||
35 | if (ch >= 0x0E00 && ch <= 0x0E5B) { | |
36 | charClass = classTable[ch - 0x0E00]; | |
37 | } | |
38 | ||
39 | return charClass; | |
40 | } | |
41 | ||
42 | ||
43 | LEUnicode ThaiShaping::leftAboveVowel(LEUnicode vowel, le_uint8 glyphSet) | |
44 | { | |
374ca955 | 45 | static const LEUnicode leftAboveVowels[][7] = { |
b75a7d8f A |
46 | {0x0E61, 0x0E32, 0x0E33, 0x0E64, 0x0E65, 0x0E66, 0x0E67}, |
47 | {0xF710, 0x0E32, 0x0E33, 0xF701, 0xF702, 0xF703, 0xF704}, | |
48 | {0xF884, 0x0E32, 0x0E33, 0xF885, 0xF886, 0xF887, 0xF788}, | |
49 | {0x0E31, 0x0E32, 0x0E33, 0x0E34, 0x0E35, 0x0E36, 0x0E37} | |
50 | }; | |
51 | ||
52 | if (vowel >= CH_MAI_HANAKAT && vowel <= CH_SARA_UEE) { | |
53 | return leftAboveVowels[glyphSet][vowel - CH_MAI_HANAKAT]; | |
54 | } | |
55 | ||
56 | if (vowel == CH_YAMAKKAN && glyphSet == 0) { | |
57 | return 0x0E7E; | |
58 | } | |
59 | ||
60 | return vowel; | |
61 | } | |
62 | ||
63 | LEUnicode ThaiShaping::lowerRightTone(LEUnicode tone, le_uint8 glyphSet) | |
64 | { | |
374ca955 | 65 | static const LEUnicode lowerRightTones[][7] = { |
b75a7d8f A |
66 | {0x0E68, 0x0E69, 0x0E6A, 0x0E6B, 0x0E6C, 0x0E6D, 0x0E6E}, |
67 | {0x0E47, 0xF70A, 0xF70B, 0xF70C, 0xF70D, 0xF70E, 0x0E4D}, | |
68 | {0x0E47, 0xF88B, 0xF88E, 0xF891, 0xF894, 0xF897, 0x0E4D}, | |
69 | {0x0E47, 0x0E48, 0x0E49, 0x0E4A, 0x0E4B, 0x0E4C, 0x0E4D} | |
70 | }; | |
71 | ||
72 | if (tone >= CH_MAITAIKHU && tone <= CH_NIKHAHIT) { | |
73 | return lowerRightTones[glyphSet][tone - CH_MAITAIKHU]; | |
74 | } | |
75 | ||
76 | return tone; | |
77 | } | |
78 | ||
79 | LEUnicode ThaiShaping::lowerLeftTone(LEUnicode tone, le_uint8 glyphSet) | |
80 | { | |
374ca955 | 81 | static const LEUnicode lowerLeftTones[][7] = { |
b75a7d8f A |
82 | {0x0E76, 0x0E77, 0x0E78, 0x0E79, 0x0E7A, 0x0E7B, 0x0E7C}, |
83 | {0xF712, 0xF705, 0xF706, 0xF707, 0xF708, 0xF709, 0xF711}, | |
84 | {0xF889, 0xF88C, 0xF88F, 0xF892, 0xF895, 0xF898, 0xF899}, | |
85 | {0x0E47, 0x0E48, 0x0E49, 0x0E4A, 0x0E4B, 0x0E4C, 0x0E4D} | |
86 | }; | |
87 | ||
88 | if (tone >= CH_MAITAIKHU && tone <= CH_NIKHAHIT) { | |
89 | return lowerLeftTones[glyphSet][tone - CH_MAITAIKHU]; | |
90 | } | |
91 | ||
92 | return tone; | |
93 | } | |
94 | ||
95 | LEUnicode ThaiShaping::upperLeftTone(LEUnicode tone, le_uint8 glyphSet) | |
96 | { | |
374ca955 | 97 | static const LEUnicode upperLeftTones[][7] = { |
b75a7d8f A |
98 | {0x0E6F, 0x0E70, 0x0E71, 0x0E72, 0x0E73, 0x0E74, 0x0E75}, |
99 | {0xF712, 0xF713, 0xF714, 0xF715, 0xF716, 0xF717, 0xF711}, | |
100 | {0xF889, 0xF88A, 0xF88D, 0xF890, 0xF893, 0xF896, 0xF899}, | |
101 | {0x0E47, 0x0E48, 0x0E49, 0x0E4A, 0x0E4B, 0x0E4C, 0x0E4D} | |
102 | }; | |
103 | ||
104 | if (tone >= CH_MAITAIKHU && tone <= CH_NIKHAHIT) { | |
105 | return upperLeftTones[glyphSet][tone - CH_MAITAIKHU]; | |
106 | } | |
107 | ||
108 | return tone; | |
109 | } | |
110 | ||
111 | LEUnicode ThaiShaping::lowerBelowVowel(LEUnicode vowel, le_uint8 glyphSet) | |
112 | { | |
374ca955 | 113 | static const LEUnicode lowerBelowVowels[][3] = { |
b75a7d8f A |
114 | {0x0E3C, 0x0E3D, 0x0E3E}, |
115 | {0xF718, 0xF719, 0xF71A}, | |
116 | {0x0E38, 0x0E39, 0x0E3A}, | |
117 | {0x0E38, 0x0E39, 0x0E3A} | |
118 | ||
119 | }; | |
120 | ||
121 | if (vowel >= CH_SARA_U && vowel <= CH_PHINTHU) { | |
122 | return lowerBelowVowels[glyphSet][vowel - CH_SARA_U]; | |
123 | } | |
124 | ||
125 | return vowel; | |
126 | } | |
127 | ||
128 | LEUnicode ThaiShaping::noDescenderCOD(LEUnicode cod, le_uint8 glyphSet) | |
129 | { | |
374ca955 | 130 | static const LEUnicode noDescenderCODs[][4] = { |
b75a7d8f A |
131 | {0x0E60, 0x0E0E, 0x0E0F, 0x0E63}, |
132 | {0xF70F, 0x0E0E, 0x0E0F, 0xF700}, | |
133 | {0x0E0D, 0x0E0E, 0x0E0F, 0x0E10}, | |
134 | {0x0E0D, 0x0E0E, 0x0E0F, 0x0E10} | |
135 | ||
136 | }; | |
137 | ||
138 | if (cod >= CH_YO_YING && cod <= CH_THO_THAN) { | |
139 | return noDescenderCODs[glyphSet][cod - CH_YO_YING]; | |
140 | } | |
141 | ||
142 | return cod; | |
143 | } | |
144 | ||
145 | le_uint8 ThaiShaping::doTransition (StateTransition transition, LEUnicode currChar, le_int32 inputIndex, le_uint8 glyphSet, | |
374ca955 | 146 | LEUnicode errorChar, LEUnicode *outputBuffer, LEGlyphStorage &glyphStorage, le_int32 &outputIndex) |
b75a7d8f | 147 | { |
374ca955 A |
148 | LEErrorCode success = LE_NO_ERROR; |
149 | ||
b75a7d8f | 150 | switch (transition.action) { |
374ca955 A |
151 | case tA: |
152 | glyphStorage.setCharIndex(outputIndex, inputIndex, success); | |
b75a7d8f A |
153 | outputBuffer[outputIndex++] = currChar; |
154 | break; | |
155 | ||
374ca955 A |
156 | case tC: |
157 | glyphStorage.setCharIndex(outputIndex, inputIndex, success); | |
b75a7d8f A |
158 | outputBuffer[outputIndex++] = currChar; |
159 | break; | |
160 | ||
374ca955 A |
161 | case tD: |
162 | glyphStorage.setCharIndex(outputIndex, inputIndex, success); | |
b75a7d8f A |
163 | outputBuffer[outputIndex++] = leftAboveVowel(currChar, glyphSet); |
164 | break; | |
165 | ||
374ca955 A |
166 | case tE: |
167 | glyphStorage.setCharIndex(outputIndex, inputIndex, success); | |
b75a7d8f A |
168 | outputBuffer[outputIndex++] = lowerRightTone(currChar, glyphSet); |
169 | break; | |
170 | ||
374ca955 A |
171 | case tF: |
172 | glyphStorage.setCharIndex(outputIndex, inputIndex, success); | |
b75a7d8f A |
173 | outputBuffer[outputIndex++] = lowerLeftTone(currChar, glyphSet); |
174 | break; | |
175 | ||
374ca955 A |
176 | case tG: |
177 | glyphStorage.setCharIndex(outputIndex, inputIndex, success); | |
b75a7d8f A |
178 | outputBuffer[outputIndex++] = upperLeftTone(currChar, glyphSet); |
179 | break; | |
180 | ||
374ca955 | 181 | case tH: |
b75a7d8f A |
182 | { |
183 | LEUnicode cod = outputBuffer[outputIndex - 1]; | |
184 | LEUnicode coa = noDescenderCOD(cod, glyphSet); | |
185 | ||
186 | if (cod != coa) { | |
187 | outputBuffer[outputIndex - 1] = coa; | |
188 | ||
374ca955 | 189 | glyphStorage.setCharIndex(outputIndex, inputIndex, success); |
b75a7d8f A |
190 | outputBuffer[outputIndex++] = currChar; |
191 | break; | |
192 | } | |
193 | ||
374ca955 | 194 | glyphStorage.setCharIndex(outputIndex, inputIndex, success); |
b75a7d8f A |
195 | outputBuffer[outputIndex++] = lowerBelowVowel(currChar, glyphSet); |
196 | break; | |
197 | } | |
198 | ||
374ca955 A |
199 | case tR: |
200 | glyphStorage.setCharIndex(outputIndex, inputIndex, success); | |
b75a7d8f A |
201 | outputBuffer[outputIndex++] = errorChar; |
202 | ||
374ca955 | 203 | glyphStorage.setCharIndex(outputIndex, inputIndex, success); |
b75a7d8f A |
204 | outputBuffer[outputIndex++] = currChar; |
205 | break; | |
206 | ||
374ca955 | 207 | case tS: |
b75a7d8f | 208 | if (currChar == CH_SARA_AM) { |
374ca955 | 209 | glyphStorage.setCharIndex(outputIndex, inputIndex, success); |
b75a7d8f A |
210 | outputBuffer[outputIndex++] = errorChar; |
211 | } | |
212 | ||
374ca955 | 213 | glyphStorage.setCharIndex(outputIndex, inputIndex, success); |
b75a7d8f A |
214 | outputBuffer[outputIndex++] = currChar; |
215 | break; | |
216 | ||
217 | default: | |
218 | // FIXME: if we get here, there's an error | |
219 | // in the state table! | |
374ca955 | 220 | glyphStorage.setCharIndex(outputIndex, inputIndex, success); |
b75a7d8f A |
221 | outputBuffer[outputIndex++] = currChar; |
222 | break; | |
223 | } | |
224 | ||
225 | return transition.nextState; | |
226 | } | |
227 | ||
228 | le_uint8 ThaiShaping::getNextState(LEUnicode ch, le_uint8 prevState, le_int32 inputIndex, le_uint8 glyphSet, LEUnicode errorChar, | |
374ca955 | 229 | le_uint8 &charClass, LEUnicode *output, LEGlyphStorage &glyphStorage, le_int32 &outputIndex) |
b75a7d8f A |
230 | { |
231 | StateTransition transition; | |
232 | ||
233 | charClass = getCharClass(ch); | |
234 | transition = getTransition(prevState, charClass); | |
235 | ||
374ca955 | 236 | return doTransition(transition, ch, inputIndex, glyphSet, errorChar, output, glyphStorage, outputIndex); |
b75a7d8f A |
237 | } |
238 | ||
239 | le_bool ThaiShaping::isLegalHere(LEUnicode ch, le_uint8 prevState) | |
240 | { | |
241 | le_uint8 charClass = getCharClass(ch); | |
242 | StateTransition transition = getTransition(prevState, charClass); | |
243 | ||
244 | switch (transition.action) { | |
374ca955 A |
245 | case tA: |
246 | case tC: | |
247 | case tD: | |
248 | case tE: | |
249 | case tF: | |
250 | case tG: | |
251 | case tH: | |
252 | return TRUE; | |
b75a7d8f | 253 | |
374ca955 A |
254 | case tR: |
255 | case tS: | |
256 | return FALSE; | |
b75a7d8f A |
257 | |
258 | default: | |
259 | // FIXME: if we get here, there's an error | |
260 | // in the state table! | |
374ca955 | 261 | return FALSE; |
b75a7d8f A |
262 | } |
263 | } | |
264 | ||
265 | le_int32 ThaiShaping::compose(const LEUnicode *input, le_int32 offset, le_int32 charCount, le_uint8 glyphSet, | |
374ca955 | 266 | LEUnicode errorChar, LEUnicode *output, LEGlyphStorage &glyphStorage) |
b75a7d8f A |
267 | { |
268 | le_uint8 state = 0; | |
269 | le_int32 inputIndex; | |
270 | le_int32 outputIndex = 0; | |
271 | le_uint8 conState = 0xFF; | |
272 | le_int32 conInput = -1; | |
273 | le_int32 conOutput = -1; | |
274 | ||
275 | for (inputIndex = 0; inputIndex < charCount; inputIndex += 1) { | |
276 | LEUnicode ch = input[inputIndex + offset]; | |
277 | le_uint8 charClass; | |
278 | ||
279 | // Decompose SARA AM into NIKHAHIT + SARA AA | |
280 | if (ch == CH_SARA_AM && isLegalHere(ch, state)) { | |
281 | outputIndex = conOutput; | |
282 | state = getNextState(CH_NIKHAHIT, conState, inputIndex, glyphSet, errorChar, charClass, | |
374ca955 | 283 | output, glyphStorage, outputIndex); |
b75a7d8f A |
284 | |
285 | for (int j = conInput + 1; j < inputIndex; j += 1) { | |
286 | ch = input[j + offset]; | |
287 | state = getNextState(ch, state, j, glyphSet, errorChar, charClass, | |
374ca955 | 288 | output, glyphStorage, outputIndex); |
b75a7d8f A |
289 | } |
290 | ||
291 | ch = CH_SARA_AA; | |
292 | } | |
293 | ||
294 | state = getNextState(ch, state, inputIndex, glyphSet, errorChar, charClass, | |
374ca955 | 295 | output, glyphStorage, outputIndex); |
b75a7d8f A |
296 | |
297 | if (charClass >= CON && charClass <= COD) { | |
298 | conState = state; | |
299 | conInput = inputIndex; | |
300 | conOutput = outputIndex; | |
301 | } | |
302 | } | |
303 | ||
304 | return outputIndex; | |
305 | } | |
306 | ||
307 | U_NAMESPACE_END |