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