]>
Commit | Line | Data |
---|---|---|
b75a7d8f | 1 | /* |
b75a7d8f | 2 | * |
73c04bcf | 3 | * (C) Copyright IBM Corp. 1998-2006 - All Rights Reserved |
b75a7d8f A |
4 | * |
5 | */ | |
6 | ||
7 | #include "LETypes.h" | |
8 | #include "OpenTypeTables.h" | |
9 | #include "OpenTypeUtilities.h" | |
10 | #include "IndicReordering.h" | |
374ca955 | 11 | #include "LEGlyphStorage.h" |
b75a7d8f A |
12 | #include "MPreFixups.h" |
13 | ||
14 | U_NAMESPACE_BEGIN | |
15 | ||
73c04bcf A |
16 | #define loclFeatureTag LE_LOCL_FEATURE_TAG |
17 | #define initFeatureTag LE_INIT_FEATURE_TAG | |
18 | #define nuktFeatureTag LE_NUKT_FEATURE_TAG | |
19 | #define akhnFeatureTag LE_AKHN_FEATURE_TAG | |
20 | #define rphfFeatureTag LE_RPHF_FEATURE_TAG | |
21 | #define blwfFeatureTag LE_BLWF_FEATURE_TAG | |
22 | #define halfFeatureTag LE_HALF_FEATURE_TAG | |
23 | #define pstfFeatureTag LE_PSTF_FEATURE_TAG | |
24 | #define vatuFeatureTag LE_VATU_FEATURE_TAG | |
25 | #define presFeatureTag LE_PRES_FEATURE_TAG | |
26 | #define blwsFeatureTag LE_BLWS_FEATURE_TAG | |
27 | #define abvsFeatureTag LE_ABVS_FEATURE_TAG | |
28 | #define pstsFeatureTag LE_PSTS_FEATURE_TAG | |
29 | #define halnFeatureTag LE_HALN_FEATURE_TAG | |
30 | ||
31 | #define blwmFeatureTag LE_BLWM_FEATURE_TAG | |
32 | #define abvmFeatureTag LE_ABVM_FEATURE_TAG | |
33 | #define distFeatureTag LE_DIST_FEATURE_TAG | |
34 | ||
35 | #define loclFeatureMask 0x80000000UL | |
36 | #define rphfFeatureMask 0x40000000UL | |
37 | #define blwfFeatureMask 0x20000000UL | |
38 | #define halfFeatureMask 0x10000000UL | |
39 | #define pstfFeatureMask 0x08000000UL | |
40 | #define nuktFeatureMask 0x04000000UL | |
41 | #define akhnFeatureMask 0x02000000UL | |
42 | #define vatuFeatureMask 0x01000000UL | |
43 | #define presFeatureMask 0x00800000UL | |
44 | #define blwsFeatureMask 0x00400000UL | |
45 | #define abvsFeatureMask 0x00200000UL | |
46 | #define pstsFeatureMask 0x00100000UL | |
47 | #define halnFeatureMask 0x00080000UL | |
48 | #define blwmFeatureMask 0x00040000UL | |
49 | #define abvmFeatureMask 0x00020000UL | |
50 | #define distFeatureMask 0x00010000UL | |
51 | #define initFeatureMask 0x00008000UL | |
52 | ||
53 | class IndicReorderingOutput : public UMemory { | |
b75a7d8f | 54 | private: |
73c04bcf | 55 | le_int32 fOutIndex; |
b75a7d8f | 56 | LEUnicode *fOutChars; |
374ca955 A |
57 | |
58 | LEGlyphStorage &fGlyphStorage; | |
b75a7d8f | 59 | |
73c04bcf A |
60 | LEUnicode fMpre; |
61 | le_int32 fMpreIndex; | |
374ca955 | 62 | |
73c04bcf A |
63 | LEUnicode fMbelow; |
64 | le_int32 fMbelowIndex; | |
374ca955 | 65 | |
73c04bcf A |
66 | LEUnicode fMabove; |
67 | le_int32 fMaboveIndex; | |
374ca955 | 68 | |
73c04bcf A |
69 | LEUnicode fMpost; |
70 | le_int32 fMpostIndex; | |
374ca955 | 71 | |
73c04bcf A |
72 | LEUnicode fLengthMark; |
73 | le_int32 fLengthMarkIndex; | |
374ca955 | 74 | |
73c04bcf A |
75 | LEUnicode fVirama; |
76 | le_int32 fViramaIndex; | |
77 | ||
78 | FeatureMask fMatraFeatures; | |
374ca955 | 79 | |
73c04bcf | 80 | le_int32 fMPreOutIndex; |
b75a7d8f | 81 | MPreFixups *fMPreFixups; |
374ca955 | 82 | |
73c04bcf A |
83 | LEUnicode fVMabove; |
84 | LEUnicode fVMpost; | |
85 | le_int32 fVMIndex; | |
86 | FeatureMask fVMFeatures; | |
374ca955 | 87 | |
73c04bcf A |
88 | LEUnicode fSMabove; |
89 | LEUnicode fSMbelow; | |
90 | le_int32 fSMIndex; | |
91 | FeatureMask fSMFeatures; | |
374ca955 A |
92 | |
93 | void saveMatra(LEUnicode matra, le_int32 matraIndex, IndicClassTable::CharClass matraClass) | |
b75a7d8f A |
94 | { |
95 | // FIXME: check if already set, or if not a matra... | |
374ca955 | 96 | if (IndicClassTable::isLengthMark(matraClass)) { |
b75a7d8f | 97 | fLengthMark = matra; |
374ca955 | 98 | fLengthMarkIndex = matraIndex; |
73c04bcf A |
99 | } else if (IndicClassTable::isVirama(matraClass)) { |
100 | fVirama = matra; | |
101 | fViramaIndex = matraIndex; | |
374ca955 | 102 | } else { |
73c04bcf A |
103 | switch (matraClass & CF_POS_MASK) { |
104 | case CF_POS_BEFORE: | |
374ca955 A |
105 | fMpre = matra; |
106 | fMpreIndex = matraIndex; | |
107 | break; | |
108 | ||
73c04bcf | 109 | case CF_POS_BELOW: |
374ca955 A |
110 | fMbelow = matra; |
111 | fMbelowIndex = matraIndex; | |
112 | break; | |
113 | ||
73c04bcf | 114 | case CF_POS_ABOVE: |
374ca955 A |
115 | fMabove = matra; |
116 | fMaboveIndex = matraIndex; | |
117 | break; | |
118 | ||
73c04bcf | 119 | case CF_POS_AFTER: |
374ca955 A |
120 | fMpost = matra; |
121 | fMpostIndex = matraIndex; | |
122 | break; | |
123 | ||
124 | default: | |
125 | // can't get here... | |
126 | break; | |
127 | } | |
b75a7d8f A |
128 | } |
129 | } | |
130 | ||
131 | public: | |
73c04bcf | 132 | IndicReorderingOutput(LEUnicode *outChars, LEGlyphStorage &glyphStorage, MPreFixups *mpreFixups) |
374ca955 A |
133 | : fOutIndex(0), fOutChars(outChars), fGlyphStorage(glyphStorage), |
134 | fMpre(0), fMpreIndex(0), fMbelow(0), fMbelowIndex(0), fMabove(0), fMaboveIndex(0), | |
73c04bcf A |
135 | fMpost(0), fMpostIndex(0), fLengthMark(0), fLengthMarkIndex(0), fVirama(0), fViramaIndex(0), |
136 | fMatraFeatures(0), fMPreOutIndex(-1), fMPreFixups(mpreFixups), | |
137 | fVMabove(0), fVMpost(0), fVMIndex(0), fVMFeatures(0), | |
138 | fSMabove(0), fSMbelow(0), fSMIndex(0), fSMFeatures(0) | |
b75a7d8f A |
139 | { |
140 | // nothing else to do... | |
141 | } | |
142 | ||
73c04bcf | 143 | ~IndicReorderingOutput() |
b75a7d8f A |
144 | { |
145 | // nothing to do here... | |
146 | } | |
147 | ||
374ca955 | 148 | void reset() |
b75a7d8f | 149 | { |
73c04bcf | 150 | fMpre = fMbelow = fMabove = fMpost = fLengthMark = fVirama = 0; |
b75a7d8f | 151 | fMPreOutIndex = -1; |
374ca955 A |
152 | |
153 | fVMabove = fVMpost = 0; | |
154 | fSMabove = fSMbelow = 0; | |
155 | } | |
156 | ||
73c04bcf A |
157 | void writeChar(LEUnicode ch, le_uint32 charIndex, FeatureMask charFeatures) |
158 | { | |
159 | LEErrorCode success = LE_NO_ERROR; | |
160 | ||
161 | fOutChars[fOutIndex] = ch; | |
162 | ||
163 | fGlyphStorage.setCharIndex(fOutIndex, charIndex, success); | |
164 | fGlyphStorage.setAuxData(fOutIndex, charFeatures, success); | |
165 | ||
166 | fOutIndex += 1; | |
167 | } | |
168 | ||
169 | le_bool noteMatra(const IndicClassTable *classTable, LEUnicode matra, le_uint32 matraIndex, FeatureMask matraFeatures, le_bool wordStart) | |
374ca955 A |
170 | { |
171 | IndicClassTable::CharClass matraClass = classTable->getCharClass(matra); | |
172 | ||
73c04bcf A |
173 | fMatraFeatures = matraFeatures; |
174 | ||
175 | if (wordStart) { | |
176 | fMatraFeatures |= initFeatureMask; | |
177 | } | |
b75a7d8f A |
178 | |
179 | if (IndicClassTable::isMatra(matraClass)) { | |
180 | if (IndicClassTable::isSplitMatra(matraClass)) { | |
181 | const SplitMatra *splitMatra = classTable->getSplitMatra(matraClass); | |
182 | int i; | |
183 | ||
184 | for (i = 0; i < 3 && (*splitMatra)[i] != 0; i += 1) { | |
185 | LEUnicode piece = (*splitMatra)[i]; | |
186 | IndicClassTable::CharClass pieceClass = classTable->getCharClass(piece); | |
187 | ||
374ca955 | 188 | saveMatra(piece, matraIndex, pieceClass); |
b75a7d8f A |
189 | } |
190 | } else { | |
374ca955 | 191 | saveMatra(matra, matraIndex, matraClass); |
b75a7d8f | 192 | } |
374ca955 A |
193 | |
194 | return TRUE; | |
195 | } | |
196 | ||
197 | return FALSE; | |
198 | } | |
199 | ||
73c04bcf | 200 | void noteVowelModifier(const IndicClassTable *classTable, LEUnicode vowelModifier, le_uint32 vowelModifierIndex, FeatureMask vowelModifierFeatures) |
374ca955 A |
201 | { |
202 | IndicClassTable::CharClass vmClass = classTable->getCharClass(vowelModifier); | |
203 | ||
204 | fVMIndex = vowelModifierIndex; | |
73c04bcf | 205 | fVMFeatures = vowelModifierFeatures; |
374ca955 A |
206 | |
207 | if (IndicClassTable::isVowelModifier(vmClass)) { | |
73c04bcf A |
208 | switch (vmClass & CF_POS_MASK) { |
209 | case CF_POS_ABOVE: | |
374ca955 A |
210 | fVMabove = vowelModifier; |
211 | break; | |
212 | ||
73c04bcf | 213 | case CF_POS_AFTER: |
374ca955 A |
214 | fVMpost = vowelModifier; |
215 | break; | |
216 | ||
217 | default: | |
218 | // FIXME: this is an error... | |
219 | break; | |
220 | } | |
221 | } | |
222 | } | |
223 | ||
73c04bcf | 224 | void noteStressMark(const IndicClassTable *classTable, LEUnicode stressMark, le_uint32 stressMarkIndex, FeatureMask stressMarkFeatures) |
374ca955 A |
225 | { |
226 | IndicClassTable::CharClass smClass = classTable->getCharClass(stressMark); | |
227 | ||
228 | fSMIndex = stressMarkIndex; | |
73c04bcf | 229 | fSMFeatures = stressMarkFeatures; |
374ca955 A |
230 | |
231 | if (IndicClassTable::isStressMark(smClass)) { | |
73c04bcf A |
232 | switch (smClass & CF_POS_MASK) { |
233 | case CF_POS_ABOVE: | |
374ca955 A |
234 | fSMabove = stressMark; |
235 | break; | |
236 | ||
73c04bcf | 237 | case CF_POS_BELOW: |
374ca955 A |
238 | fSMbelow = stressMark; |
239 | break; | |
240 | ||
241 | default: | |
242 | // FIXME: this is an error... | |
243 | break; | |
244 | } | |
b75a7d8f A |
245 | } |
246 | } | |
247 | ||
248 | void noteBaseConsonant() | |
249 | { | |
250 | if (fMPreFixups != NULL && fMPreOutIndex >= 0) { | |
251 | fMPreFixups->add(fOutIndex, fMPreOutIndex); | |
252 | } | |
253 | } | |
254 | ||
73c04bcf A |
255 | // Handles virama in Sinhala split vowels. |
256 | void writeVirama() | |
257 | { | |
258 | if (fVirama != 0) { | |
259 | writeChar(fVirama, fViramaIndex, fMatraFeatures); | |
260 | } | |
261 | } | |
262 | ||
b75a7d8f A |
263 | void writeMpre() |
264 | { | |
265 | if (fMpre != 0) { | |
266 | fMPreOutIndex = fOutIndex; | |
73c04bcf | 267 | writeChar(fMpre, fMpreIndex, fMatraFeatures); |
b75a7d8f A |
268 | } |
269 | } | |
270 | ||
271 | void writeMbelow() | |
272 | { | |
273 | if (fMbelow != 0) { | |
73c04bcf | 274 | writeChar(fMbelow, fMbelowIndex, fMatraFeatures); |
b75a7d8f A |
275 | } |
276 | } | |
277 | ||
278 | void writeMabove() | |
279 | { | |
280 | if (fMabove != 0) { | |
73c04bcf | 281 | writeChar(fMabove, fMaboveIndex, fMatraFeatures); |
b75a7d8f A |
282 | } |
283 | } | |
284 | ||
285 | void writeMpost() | |
286 | { | |
287 | if (fMpost != 0) { | |
73c04bcf | 288 | writeChar(fMpost, fMpostIndex, fMatraFeatures); |
b75a7d8f A |
289 | } |
290 | } | |
291 | ||
292 | void writeLengthMark() | |
293 | { | |
294 | if (fLengthMark != 0) { | |
73c04bcf | 295 | writeChar(fLengthMark, fLengthMarkIndex, fMatraFeatures); |
b75a7d8f A |
296 | } |
297 | } | |
374ca955 A |
298 | |
299 | void writeVMabove() | |
300 | { | |
301 | if (fVMabove != 0) { | |
73c04bcf | 302 | writeChar(fVMabove, fVMIndex, fVMFeatures); |
374ca955 A |
303 | } |
304 | } | |
305 | ||
306 | void writeVMpost() | |
307 | { | |
308 | if (fVMpost != 0) { | |
73c04bcf | 309 | writeChar(fVMpost, fVMIndex, fVMFeatures); |
374ca955 A |
310 | } |
311 | } | |
312 | ||
313 | void writeSMabove() | |
314 | { | |
315 | if (fSMabove != 0) { | |
73c04bcf | 316 | writeChar(fSMabove, fSMIndex, fSMFeatures); |
374ca955 A |
317 | } |
318 | } | |
319 | ||
320 | void writeSMbelow() | |
321 | { | |
322 | if (fSMbelow != 0) { | |
73c04bcf | 323 | writeChar(fSMbelow, fSMIndex, fSMFeatures); |
374ca955 A |
324 | } |
325 | } | |
326 | ||
b75a7d8f A |
327 | le_int32 getOutputIndex() |
328 | { | |
329 | return fOutIndex; | |
330 | } | |
331 | }; | |
332 | ||
333 | enum | |
334 | { | |
335 | C_DOTTED_CIRCLE = 0x25CC | |
336 | }; | |
337 | ||
73c04bcf A |
338 | // TODO: Find better names for these! |
339 | #define tagArray4 (loclFeatureMask | nuktFeatureMask | akhnFeatureMask | vatuFeatureMask | presFeatureMask | blwsFeatureMask | abvsFeatureMask | pstsFeatureMask | halnFeatureMask | blwmFeatureMask | abvmFeatureMask | distFeatureMask) | |
340 | #define tagArray3 (pstfFeatureMask | tagArray4) | |
341 | #define tagArray2 (halfFeatureMask | tagArray3) | |
342 | #define tagArray1 (blwfFeatureMask | tagArray2) | |
343 | #define tagArray0 (rphfFeatureMask | tagArray1) | |
b75a7d8f | 344 | |
73c04bcf | 345 | static const FeatureMap featureMap[] = |
b75a7d8f | 346 | { |
73c04bcf A |
347 | {loclFeatureTag, loclFeatureMask}, |
348 | {initFeatureTag, initFeatureMask}, | |
349 | {nuktFeatureTag, nuktFeatureMask}, | |
350 | {akhnFeatureTag, akhnFeatureMask}, | |
351 | {rphfFeatureTag, rphfFeatureMask}, | |
352 | {blwfFeatureTag, blwfFeatureMask}, | |
353 | {halfFeatureTag, halfFeatureMask}, | |
354 | {pstfFeatureTag, pstfFeatureMask}, | |
355 | {vatuFeatureTag, vatuFeatureMask}, | |
356 | {presFeatureTag, presFeatureMask}, | |
357 | {blwsFeatureTag, blwsFeatureMask}, | |
358 | {abvsFeatureTag, abvsFeatureMask}, | |
359 | {pstsFeatureTag, pstsFeatureMask}, | |
360 | {halnFeatureTag, halnFeatureMask}, | |
361 | {blwmFeatureTag, blwmFeatureMask}, | |
362 | {abvmFeatureTag, abvmFeatureMask}, | |
363 | {distFeatureTag, distFeatureMask} | |
b75a7d8f A |
364 | }; |
365 | ||
73c04bcf | 366 | static const le_int32 featureCount = LE_ARRAY_SIZE(featureMap); |
b75a7d8f | 367 | |
73c04bcf A |
368 | static const le_int8 stateTable[][CC_COUNT] = |
369 | { | |
370 | // xx vm sm iv i2 i3 ct cn nu dv s1 s2 s3 vr zw | |
371 | { 1, 6, 1, 5, 8, 11, 3, 2, 1, 5, 9, 5, 5, 1, 1}, // 0 - ground state | |
372 | {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, // 1 - exit state | |
373 | {-1, 6, 1, -1, -1, -1, -1, -1, -1, 5, 9, 5, 5, 4, 12}, // 2 - consonant with nukta | |
374 | {-1, 6, 1, -1, -1, -1, -1, -1, 2, 5, 9, 5, 5, 4, 12}, // 3 - consonant | |
375 | {-1, -1, -1, -1, -1, -1, 3, 2, -1, -1, -1, -1, -1, -1, 7}, // 4 - consonant virama | |
376 | {-1, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, // 5 - dependent vowels | |
377 | {-1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, // 6 - vowel mark | |
378 | {-1, -1, -1, -1, -1, -1, 3, 2, -1, -1, -1, -1, -1, -1, -1}, // 7 - consonant virama ZWJ, consonant ZWJ virama | |
379 | {-1, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4, -1}, // 8 - independent vowels that can take a virama | |
380 | {-1, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, 10, 5, -1, -1}, // 9 - first part of split vowel | |
381 | {-1, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 5, -1, -1}, // 10 - second part of split vowel | |
382 | {-1, 6, 1, -1, -1, -1, -1, -1, -1, 5, 9, 5, 5, 4, -1}, // 11 - independent vowels that can take an iv | |
383 | {-1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 7, -1} // 12 - consonant ZWJ (TODO: Take everything else that can be after a consonant?) | |
b75a7d8f A |
384 | }; |
385 | ||
73c04bcf A |
386 | |
387 | const FeatureMap *IndicReordering::getFeatureMap(le_int32 &count) | |
b75a7d8f | 388 | { |
73c04bcf A |
389 | count = featureCount; |
390 | ||
391 | return featureMap; | |
b75a7d8f A |
392 | } |
393 | ||
394 | le_int32 IndicReordering::findSyllable(const IndicClassTable *classTable, const LEUnicode *chars, le_int32 prev, le_int32 charCount) | |
395 | { | |
396 | le_int32 cursor = prev; | |
397 | le_int8 state = 0; | |
398 | ||
399 | while (cursor < charCount) { | |
400 | IndicClassTable::CharClass charClass = classTable->getCharClass(chars[cursor]); | |
401 | ||
73c04bcf | 402 | state = stateTable[state][charClass & CF_CLASS_MASK]; |
b75a7d8f A |
403 | |
404 | if (state < 0) { | |
405 | break; | |
406 | } | |
407 | ||
408 | cursor += 1; | |
409 | } | |
410 | ||
411 | return cursor; | |
412 | } | |
413 | ||
414 | le_int32 IndicReordering::reorder(const LEUnicode *chars, le_int32 charCount, le_int32 scriptCode, | |
374ca955 | 415 | LEUnicode *outChars, LEGlyphStorage &glyphStorage, |
b75a7d8f A |
416 | MPreFixups **outMPreFixups) |
417 | { | |
418 | MPreFixups *mpreFixups = NULL; | |
419 | const IndicClassTable *classTable = IndicClassTable::getScriptClassTable(scriptCode); | |
420 | ||
73c04bcf | 421 | if (classTable->scriptFlags & SF_MPRE_FIXUP) { |
b75a7d8f A |
422 | mpreFixups = new MPreFixups(charCount); |
423 | } | |
424 | ||
73c04bcf | 425 | IndicReorderingOutput output(outChars, glyphStorage, mpreFixups); |
b75a7d8f | 426 | le_int32 i, prev = 0; |
73c04bcf | 427 | le_bool lastInWord = FALSE; |
b75a7d8f A |
428 | |
429 | while (prev < charCount) { | |
430 | le_int32 syllable = findSyllable(classTable, chars, prev, charCount); | |
374ca955 | 431 | le_int32 matra, markStart = syllable; |
b75a7d8f | 432 | |
374ca955 A |
433 | output.reset(); |
434 | ||
435 | if (classTable->isStressMark(chars[markStart - 1])) { | |
436 | markStart -= 1; | |
73c04bcf | 437 | output.noteStressMark(classTable, chars[markStart], markStart, tagArray1); |
b75a7d8f | 438 | } |
374ca955 | 439 | |
73c04bcf | 440 | if (markStart != prev && classTable->isVowelModifier(chars[markStart - 1])) { |
374ca955 | 441 | markStart -= 1; |
73c04bcf | 442 | output.noteVowelModifier(classTable, chars[markStart], markStart, tagArray1); |
b75a7d8f A |
443 | } |
444 | ||
374ca955 A |
445 | matra = markStart - 1; |
446 | ||
73c04bcf | 447 | while (output.noteMatra(classTable, chars[matra], matra, tagArray1, !lastInWord) && matra != prev) { |
374ca955 A |
448 | matra -= 1; |
449 | } | |
b75a7d8f | 450 | |
73c04bcf A |
451 | lastInWord = TRUE; |
452 | ||
453 | switch (classTable->getCharClass(chars[prev]) & CF_CLASS_MASK) { | |
454 | case CC_RESERVED: | |
455 | lastInWord = FALSE; | |
456 | /* fall through */ | |
457 | ||
458 | case CC_INDEPENDENT_VOWEL: | |
459 | case CC_ZERO_WIDTH_MARK: | |
b75a7d8f | 460 | for (i = prev; i < syllable; i += 1) { |
73c04bcf | 461 | output.writeChar(chars[i], i, tagArray1); |
b75a7d8f A |
462 | } |
463 | ||
464 | break; | |
465 | ||
73c04bcf A |
466 | case CC_NUKTA: |
467 | case CC_VIRAMA: | |
468 | output.writeChar(C_DOTTED_CIRCLE, prev, tagArray1); | |
469 | output.writeChar(chars[prev], prev, tagArray1); | |
b75a7d8f A |
470 | break; |
471 | ||
73c04bcf A |
472 | case CC_DEPENDENT_VOWEL: |
473 | case CC_SPLIT_VOWEL_PIECE_1: | |
474 | case CC_SPLIT_VOWEL_PIECE_2: | |
475 | case CC_SPLIT_VOWEL_PIECE_3: | |
476 | case CC_VOWEL_MODIFIER: | |
477 | case CC_STRESS_MARK: | |
b75a7d8f | 478 | output.writeMpre(); |
374ca955 | 479 | |
73c04bcf | 480 | output.writeChar(C_DOTTED_CIRCLE, prev, tagArray1); |
374ca955 | 481 | |
b75a7d8f | 482 | output.writeMbelow(); |
374ca955 | 483 | output.writeSMbelow(); |
b75a7d8f | 484 | output.writeMabove(); |
374ca955 | 485 | |
73c04bcf | 486 | if ((classTable->scriptFlags & SF_MATRAS_AFTER_BASE) != 0) { |
374ca955 A |
487 | output.writeMpost(); |
488 | } | |
489 | ||
73c04bcf | 490 | if ((classTable->scriptFlags & SF_REPH_AFTER_BELOW) != 0) { |
374ca955 A |
491 | output.writeVMabove(); |
492 | output.writeSMabove(); // FIXME: there are no SM's in these scripts... | |
493 | } | |
494 | ||
73c04bcf | 495 | if ((classTable->scriptFlags & SF_MATRAS_AFTER_BASE) == 0) { |
374ca955 A |
496 | output.writeMpost(); |
497 | } | |
498 | ||
b75a7d8f | 499 | output.writeLengthMark(); |
73c04bcf | 500 | output.writeVirama(); |
374ca955 | 501 | |
73c04bcf | 502 | if ((classTable->scriptFlags & SF_REPH_AFTER_BELOW) == 0) { |
374ca955 A |
503 | output.writeVMabove(); |
504 | output.writeSMabove(); | |
505 | } | |
506 | ||
507 | output.writeVMpost(); | |
b75a7d8f A |
508 | break; |
509 | ||
73c04bcf A |
510 | case CC_INDEPENDENT_VOWEL_2: |
511 | case CC_INDEPENDENT_VOWEL_3: | |
512 | case CC_CONSONANT: | |
513 | case CC_CONSONANT_WITH_NUKTA: | |
b75a7d8f | 514 | { |
374ca955 A |
515 | le_uint32 length = markStart - prev; |
516 | le_int32 lastConsonant = markStart - 1; | |
b75a7d8f A |
517 | le_int32 baseLimit = prev; |
518 | ||
519 | // Check for REPH at front of syllable | |
520 | if (length > 2 && classTable->isReph(chars[prev]) && classTable->isVirama(chars[prev + 1])) { | |
521 | baseLimit += 2; | |
522 | ||
523 | // Check for eyelash RA, if the script supports it | |
73c04bcf | 524 | if ((classTable->scriptFlags & SF_EYELASH_RA) != 0 && |
b75a7d8f A |
525 | chars[baseLimit] == C_SIGN_ZWJ) { |
526 | if (length > 3) { | |
527 | baseLimit += 1; | |
528 | } else { | |
529 | baseLimit -= 2; | |
530 | } | |
531 | } | |
532 | } | |
533 | ||
534 | while (lastConsonant > baseLimit && !classTable->isConsonant(chars[lastConsonant])) { | |
535 | lastConsonant -= 1; | |
536 | } | |
537 | ||
538 | le_int32 baseConsonant = lastConsonant; | |
539 | le_int32 postBase = lastConsonant + 1; | |
73c04bcf | 540 | le_int32 postBaseLimit = classTable->scriptFlags & SF_POST_BASE_LIMIT_MASK; |
374ca955 A |
541 | le_bool seenVattu = FALSE; |
542 | le_bool seenBelowBaseForm = FALSE; | |
543 | ||
73c04bcf | 544 | if (postBase < markStart && classTable->isNukta(chars[postBase])) { |
374ca955 A |
545 | postBase += 1; |
546 | } | |
b75a7d8f A |
547 | |
548 | while (baseConsonant > baseLimit) { | |
549 | IndicClassTable::CharClass charClass = classTable->getCharClass(chars[baseConsonant]); | |
550 | ||
551 | if (IndicClassTable::isConsonant(charClass)) { | |
552 | if (postBaseLimit == 0 || seenVattu || | |
553 | (baseConsonant > baseLimit && !classTable->isVirama(chars[baseConsonant - 1])) || | |
554 | !IndicClassTable::hasPostOrBelowBaseForm(charClass)) { | |
555 | break; | |
556 | } | |
557 | ||
558 | seenVattu = IndicClassTable::isVattu(charClass); | |
559 | ||
560 | if (IndicClassTable::hasPostBaseForm(charClass)) { | |
561 | if (seenBelowBaseForm) { | |
562 | break; | |
563 | } | |
564 | ||
565 | postBase = baseConsonant; | |
566 | } else if (IndicClassTable::hasBelowBaseForm(charClass)) { | |
374ca955 | 567 | seenBelowBaseForm = TRUE; |
b75a7d8f A |
568 | } |
569 | ||
570 | postBaseLimit -= 1; | |
571 | } | |
572 | ||
573 | baseConsonant -= 1; | |
574 | } | |
575 | ||
576 | // Write Mpre | |
577 | output.writeMpre(); | |
578 | ||
579 | // Write eyelash RA | |
580 | // NOTE: baseLimit == prev + 3 iff eyelash RA present... | |
581 | if (baseLimit == prev + 3) { | |
73c04bcf A |
582 | output.writeChar(chars[prev], prev, tagArray2); |
583 | output.writeChar(chars[prev + 1], prev + 1, tagArray2); | |
584 | output.writeChar(chars[prev + 2], prev + 2, tagArray2); | |
b75a7d8f A |
585 | } |
586 | ||
587 | // write any pre-base consonants | |
374ca955 | 588 | le_bool supressVattu = TRUE; |
b75a7d8f A |
589 | |
590 | for (i = baseLimit; i < baseConsonant; i += 1) { | |
591 | LEUnicode ch = chars[i]; | |
374ca955 | 592 | // Don't put 'blwf' on first consonant. |
73c04bcf | 593 | FeatureMask features = (i == baseLimit? tagArray2 : tagArray1); |
b75a7d8f A |
594 | IndicClassTable::CharClass charClass = classTable->getCharClass(ch); |
595 | ||
596 | if (IndicClassTable::isConsonant(charClass)) { | |
597 | if (IndicClassTable::isVattu(charClass) && supressVattu) { | |
73c04bcf | 598 | features = tagArray4; |
b75a7d8f A |
599 | } |
600 | ||
601 | supressVattu = IndicClassTable::isVattu(charClass); | |
602 | } else if (IndicClassTable::isVirama(charClass) && chars[i + 1] == C_SIGN_ZWNJ) | |
603 | { | |
73c04bcf | 604 | features = tagArray4; |
b75a7d8f A |
605 | } |
606 | ||
73c04bcf | 607 | output.writeChar(ch, i, features); |
b75a7d8f A |
608 | } |
609 | ||
610 | le_int32 bcSpan = baseConsonant + 1; | |
611 | ||
374ca955 | 612 | if (bcSpan < markStart && classTable->isNukta(chars[bcSpan])) { |
b75a7d8f A |
613 | bcSpan += 1; |
614 | } | |
615 | ||
374ca955 | 616 | if (baseConsonant == lastConsonant && bcSpan < markStart && classTable->isVirama(chars[bcSpan])) { |
b75a7d8f A |
617 | bcSpan += 1; |
618 | ||
374ca955 | 619 | if (bcSpan < markStart && chars[bcSpan] == C_SIGN_ZWNJ) { |
b75a7d8f A |
620 | bcSpan += 1; |
621 | } | |
622 | } | |
623 | ||
624 | // note the base consonant for post-GSUB fixups | |
625 | output.noteBaseConsonant(); | |
626 | ||
627 | // write base consonant | |
628 | for (i = baseConsonant; i < bcSpan; i += 1) { | |
73c04bcf | 629 | output.writeChar(chars[i], i, tagArray4); |
b75a7d8f A |
630 | } |
631 | ||
73c04bcf | 632 | if ((classTable->scriptFlags & SF_MATRAS_AFTER_BASE) != 0) { |
b75a7d8f | 633 | output.writeMbelow(); |
374ca955 | 634 | output.writeSMbelow(); // FIXME: there are no SMs in these scripts... |
b75a7d8f A |
635 | output.writeMabove(); |
636 | output.writeMpost(); | |
637 | } | |
638 | ||
639 | // write below-base consonants | |
640 | if (baseConsonant != lastConsonant) { | |
641 | for (i = bcSpan + 1; i < postBase; i += 1) { | |
73c04bcf | 642 | output.writeChar(chars[i], i, tagArray1); |
b75a7d8f A |
643 | } |
644 | ||
645 | if (postBase > lastConsonant) { | |
646 | // write halant that was after base consonant | |
73c04bcf | 647 | output.writeChar(chars[bcSpan], bcSpan, tagArray1); |
b75a7d8f A |
648 | } |
649 | } | |
650 | ||
374ca955 | 651 | // write Mbelow, SMbelow, Mabove |
73c04bcf | 652 | if ((classTable->scriptFlags & SF_MATRAS_AFTER_BASE) == 0) { |
b75a7d8f | 653 | output.writeMbelow(); |
374ca955 | 654 | output.writeSMbelow(); |
b75a7d8f A |
655 | output.writeMabove(); |
656 | } | |
657 | ||
73c04bcf | 658 | if ((classTable->scriptFlags & SF_REPH_AFTER_BELOW) != 0) { |
b75a7d8f | 659 | if (baseLimit == prev + 2) { |
73c04bcf A |
660 | output.writeChar(chars[prev], prev, tagArray0); |
661 | output.writeChar(chars[prev + 1], prev + 1, tagArray0); | |
b75a7d8f A |
662 | } |
663 | ||
374ca955 A |
664 | output.writeVMabove(); |
665 | output.writeSMabove(); // FIXME: there are no SM's in these scripts... | |
b75a7d8f A |
666 | } |
667 | ||
668 | // write post-base consonants | |
669 | // FIXME: does this put the right tags on post-base consonants? | |
670 | if (baseConsonant != lastConsonant) { | |
671 | if (postBase <= lastConsonant) { | |
672 | for (i = postBase; i <= lastConsonant; i += 1) { | |
73c04bcf | 673 | output.writeChar(chars[i], i, tagArray3); |
b75a7d8f A |
674 | } |
675 | ||
676 | // write halant that was after base consonant | |
73c04bcf | 677 | output.writeChar(chars[bcSpan], bcSpan, tagArray1); |
b75a7d8f A |
678 | } |
679 | ||
680 | // write the training halant, if there is one | |
681 | if (lastConsonant < matra && classTable->isVirama(chars[matra])) { | |
73c04bcf | 682 | output.writeChar(chars[matra], matra, tagArray4); |
b75a7d8f A |
683 | } |
684 | } | |
685 | ||
686 | // write Mpost | |
73c04bcf | 687 | if ((classTable->scriptFlags & SF_MATRAS_AFTER_BASE) == 0) { |
b75a7d8f A |
688 | output.writeMpost(); |
689 | } | |
690 | ||
691 | output.writeLengthMark(); | |
73c04bcf | 692 | output.writeVirama(); |
b75a7d8f A |
693 | |
694 | // write reph | |
73c04bcf | 695 | if ((classTable->scriptFlags & SF_REPH_AFTER_BELOW) == 0) { |
b75a7d8f | 696 | if (baseLimit == prev + 2) { |
73c04bcf A |
697 | output.writeChar(chars[prev], prev, tagArray0); |
698 | output.writeChar(chars[prev + 1], prev + 1, tagArray0); | |
b75a7d8f A |
699 | } |
700 | ||
374ca955 A |
701 | output.writeVMabove(); |
702 | output.writeSMabove(); | |
b75a7d8f A |
703 | } |
704 | ||
374ca955 | 705 | output.writeVMpost(); |
b75a7d8f A |
706 | |
707 | break; | |
708 | } | |
709 | ||
710 | default: | |
711 | break; | |
712 | } | |
713 | ||
714 | prev = syllable; | |
715 | } | |
716 | ||
717 | *outMPreFixups = mpreFixups; | |
718 | ||
719 | return output.getOutputIndex(); | |
720 | } | |
721 | ||
374ca955 | 722 | void IndicReordering::adjustMPres(MPreFixups *mpreFixups, LEGlyphStorage &glyphStorage) |
b75a7d8f A |
723 | { |
724 | if (mpreFixups != NULL) { | |
374ca955 | 725 | mpreFixups->apply(glyphStorage); |
b75a7d8f A |
726 | |
727 | delete mpreFixups; | |
728 | } | |
729 | } | |
730 | ||
731 | U_NAMESPACE_END |