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