3  * (C) Copyright IBM Corp. 1998-2007 - All Rights Reserved  
   5  * This file is a modification of the ICU file IndicReordering.cpp 
   6  * by Jens Herden and Javier Sola for Khmer language  
  11 #include "OpenTypeTables.h" 
  12 #include "KhmerReordering.h" 
  13 #include "LEGlyphStorage.h" 
  18 // Characters that get refered to by name... 
  23     C_DOTTED_CIRCLE 
= 0x25CC, 
  26     C_SIGN_NIKAHIT  
= 0x17C6, 
  34     // simple classes, they are used in the statetable (in this file) to control the length of a syllable 
  35     // they are also used to know where a character should be placed (location in reference to the base character) 
  36     // and also to know if a character, when independtly displayed, should be displayed with a dotted-circle to 
  37     // indicate error in syllable construction  
  38     _xx 
= KhmerClassTable::CC_RESERVED
, 
  39     _sa 
= KhmerClassTable::CC_SIGN_ABOVE 
| KhmerClassTable::CF_DOTTED_CIRCLE 
| KhmerClassTable::CF_POS_ABOVE
, 
  40     _sp 
= KhmerClassTable::CC_SIGN_AFTER 
| KhmerClassTable::CF_DOTTED_CIRCLE
| KhmerClassTable::CF_POS_AFTER
, 
  41     _c1 
= KhmerClassTable::CC_CONSONANT 
| KhmerClassTable::CF_CONSONANT
, 
  42     _c2 
= KhmerClassTable::CC_CONSONANT2 
| KhmerClassTable::CF_CONSONANT
, 
  43     _c3 
= KhmerClassTable::CC_CONSONANT3 
| KhmerClassTable::CF_CONSONANT
, 
  44     _rb 
= KhmerClassTable::CC_ROBAT 
| KhmerClassTable::CF_POS_ABOVE 
| KhmerClassTable::CF_DOTTED_CIRCLE
, 
  45     _cs 
= KhmerClassTable::CC_CONSONANT_SHIFTER 
| KhmerClassTable::CF_DOTTED_CIRCLE 
| KhmerClassTable::CF_SHIFTER
, 
  46     _dl 
= KhmerClassTable::CC_DEPENDENT_VOWEL 
| KhmerClassTable::CF_POS_BEFORE 
| KhmerClassTable::CF_DOTTED_CIRCLE
,  
  47     _db 
= KhmerClassTable::CC_DEPENDENT_VOWEL 
| KhmerClassTable::CF_POS_BELOW 
| KhmerClassTable::CF_DOTTED_CIRCLE
, 
  48     _da 
= KhmerClassTable::CC_DEPENDENT_VOWEL 
| KhmerClassTable::CF_POS_ABOVE 
| KhmerClassTable::CF_DOTTED_CIRCLE 
| KhmerClassTable::CF_ABOVE_VOWEL
, 
  49     _dr 
= KhmerClassTable::CC_DEPENDENT_VOWEL 
| KhmerClassTable::CF_POS_AFTER 
| KhmerClassTable::CF_DOTTED_CIRCLE
, 
  50     _co 
= KhmerClassTable::CC_COENG 
| KhmerClassTable::CF_COENG 
| KhmerClassTable::CF_DOTTED_CIRCLE
, 
  53     _va 
= _da 
| KhmerClassTable::CF_SPLIT_VOWEL
, 
  54     _vr 
= _dr 
| KhmerClassTable::CF_SPLIT_VOWEL
 
  58 // Character class tables  
  59 // _xx character does not combine into syllable, such as numbers, puntuation marks, non-Khmer signs...  
  60 // _sa Sign placed above the base 
  61 // _sp Sign placed after the base 
  62 // _c1 Consonant of type 1 or independent vowel (independent vowels behave as type 1 consonants) 
  63 // _c2 Consonant of type 2 (only RO) 
  64 // _c3 Consonant of type 3  
  65 // _rb Khmer sign robat u17CC. combining mark for subscript consonants 
  66 // _cd Consonant-shifter 
  67 // _dl Dependent vowel placed before the base (left of the base) 
  68 // _db Dependent vowel placed below the base  
  69 // _da Dependent vowel placed above the base 
  70 // _dr Dependent vowel placed behind the base (right of the base)     
  71 // _co Khmer combining mark COENG u17D2, combines with the consonant or independent vowel following  
  72 //     it to create a subscript consonant or independent vowel    
  73 // _va Khmer split vowel in wich the first part is before the base and the second one above the base 
  74 // _vr Khmer split vowel in wich the first part is before the base and the second one behind (right of) the base 
  76 static const KhmerClassTable::CharClass khmerCharClasses
[] = 
  78     _c1
, _c1
, _c1
, _c3
, _c1
, _c1
, _c1
, _c1
, _c3
, _c1
, _c1
, _c1
, _c1
, _c3
, _c1
, _c1
, // 1780 - 178F 
  79     _c1
, _c1
, _c1
, _c1
, _c3
, _c1
, _c1
, _c1
, _c1
, _c3
, _c2
, _c1
, _c1
, _c1
, _c3
, _c3
, // 1790 - 179F 
  80     _c1
, _c3
, _c1
, _c1
, _c1
, _c1
, _c1
, _c1
, _c1
, _c1
, _c1
, _c1
, _c1
, _c1
, _c1
, _c1
, // 17A0 - 17AF 
  81     _c1
, _c1
, _c1
, _c1
, _dr
, _dr
, _dr
, _da
, _da
, _da
, _da
, _db
, _db
, _db
, _va
, _vr
, // 17B0 - 17BF 
  82     _vr
, _dl
, _dl
, _dl
, _vr
, _vr
, _sa
, _sp
, _sp
, _cs
, _cs
, _sa
, _rb
, _sa
, _sa
, _sa
, // 17C0 - 17CF 
  83     _sa
, _sa
, _co
, _sa
, _xx
, _xx
, _xx
, _xx
, _xx
, _xx
, _xx
, _xx
, _xx
, _sa
, _xx
, _xx
, // 17D0 - 17DF 
  92 // The range of characters defined in the above table is defined here. FOr Khmer 1780 to 17DF 
  93 // Even if the Khmer range is bigger, all other characters are not combinable, and therefore treated 
  95 static const KhmerClassTable khmerClassTable 
= {0x1780, 0x17df, khmerCharClasses
}; 
  98 // Below we define how a character in the input string is either in the khmerCharClasses table 
  99 // (in which case we get its type back), a ZWJ or ZWNJ (two characters that may appear  
 100 // within the syllable, but are not in the table) we also get their type back, or an unknown object 
 101 // in which case we get _xx (CC_RESERVED) back 
 102 KhmerClassTable::CharClass 
KhmerClassTable::getCharClass(LEUnicode ch
) const 
 105     if (ch 
== C_SIGN_ZWJ
) { 
 106         return CC_ZERO_WIDTH_J_MARK
; 
 109     if (ch 
== C_SIGN_ZWNJ
) { 
 110         return CC_ZERO_WIDTH_NJ_MARK
; 
 113     if (ch 
< firstChar 
|| ch 
> lastChar
) { 
 117     return classTable
[ch 
- firstChar
]; 
 120 const KhmerClassTable 
*KhmerClassTable::getKhmerClassTable() 
 122     return &khmerClassTable
; 
 127 class KhmerReorderingOutput 
: public UMemory 
{ 
 129     le_int32 fSyllableCount
; 
 131     LEUnicode 
*fOutChars
; 
 133     LEGlyphStorage 
&fGlyphStorage
; 
 137     KhmerReorderingOutput(LEUnicode 
*outChars
, LEGlyphStorage 
&glyphStorage
) 
 138         : fSyllableCount(0), fOutIndex(0), fOutChars(outChars
), fGlyphStorage(glyphStorage
) 
 140         // nothing else to do... 
 143     ~KhmerReorderingOutput() 
 145         // nothing to do here... 
 153     void writeChar(LEUnicode ch
, le_uint32 charIndex
, FeatureMask charFeatures
) 
 155         LEErrorCode success 
= LE_NO_ERROR
; 
 157         fOutChars
[fOutIndex
] = ch
; 
 159         fGlyphStorage
.setCharIndex(fOutIndex
, charIndex
, success
); 
 160         fGlyphStorage
.setAuxData(fOutIndex
, charFeatures 
| (fSyllableCount 
& LE_GLYPH_GROUP_MASK
), success
); 
 165     le_int32 
getOutputIndex() 
 172 #define blwfFeatureTag LE_BLWF_FEATURE_TAG 
 173 #define pstfFeatureTag LE_PSTF_FEATURE_TAG 
 174 #define presFeatureTag LE_PRES_FEATURE_TAG 
 175 #define blwsFeatureTag LE_BLWS_FEATURE_TAG 
 176 #define abvsFeatureTag LE_ABVS_FEATURE_TAG 
 177 #define pstsFeatureTag LE_PSTS_FEATURE_TAG 
 179 #define blwmFeatureTag LE_BLWM_FEATURE_TAG 
 180 #define abvmFeatureTag LE_ABVM_FEATURE_TAG 
 181 #define distFeatureTag LE_DIST_FEATURE_TAG 
 183 #define prefFeatureTag LE_PREF_FEATURE_TAG 
 184 #define abvfFeatureTag LE_ABVF_FEATURE_TAG 
 185 #define cligFeatureTag LE_CLIG_FEATURE_TAG 
 186 #define mkmkFeatureTag LE_MKMK_FEATURE_TAG 
 188 #define prefFeatureMask 0x80000000UL 
 189 #define blwfFeatureMask 0x40000000UL 
 190 #define abvfFeatureMask 0x20000000UL 
 191 #define pstfFeatureMask 0x10000000UL  
 192 #define presFeatureMask 0x08000000UL 
 193 #define blwsFeatureMask 0x04000000UL 
 194 #define abvsFeatureMask 0x02000000UL 
 195 #define pstsFeatureMask 0x01000000UL 
 196 #define cligFeatureMask 0x00800000UL 
 197 #define distFeatureMask 0x00400000UL 
 198 #define blwmFeatureMask 0x00200000UL 
 199 #define abvmFeatureMask 0x00100000UL 
 200 #define mkmkFeatureMask 0x00080000UL 
 202 #define tagPref    (prefFeatureMask | presFeatureMask | cligFeatureMask | distFeatureMask) 
 203 #define tagAbvf    (abvfFeatureMask | abvsFeatureMask | cligFeatureMask | distFeatureMask | abvmFeatureMask | mkmkFeatureMask) 
 204 #define tagPstf    (blwfFeatureMask | blwsFeatureMask | prefFeatureMask | presFeatureMask | pstfFeatureMask | pstsFeatureMask | cligFeatureMask | distFeatureMask | blwmFeatureMask) 
 205 #define tagBlwf    (blwfFeatureMask | blwsFeatureMask | cligFeatureMask | distFeatureMask | blwmFeatureMask | mkmkFeatureMask) 
 206 #define tagDefault (prefFeatureMask | blwfFeatureMask | presFeatureMask | blwsFeatureMask | cligFeatureMask | distFeatureMask | abvmFeatureMask | blwmFeatureMask | mkmkFeatureMask) 
 210 // These are in the order in which the features need to be applied 
 211 // for correct processing 
 212 static const FeatureMap featureMap
[] = 
 215     {prefFeatureTag
, prefFeatureMask
}, 
 216     {blwfFeatureTag
, blwfFeatureMask
}, 
 217     {abvfFeatureTag
, abvfFeatureMask
}, 
 218     {pstfFeatureTag
, pstfFeatureMask
},  
 219     {presFeatureTag
, presFeatureMask
}, 
 220     {blwsFeatureTag
, blwsFeatureMask
}, 
 221     {abvsFeatureTag
, abvsFeatureMask
}, 
 222     {pstsFeatureTag
, pstsFeatureMask
}, 
 223     {cligFeatureTag
, cligFeatureMask
}, 
 225     // Positioning features 
 226     {distFeatureTag
, distFeatureMask
}, 
 227     {blwmFeatureTag
, blwmFeatureMask
}, 
 228     {abvmFeatureTag
, abvmFeatureMask
}, 
 229     {mkmkFeatureTag
, mkmkFeatureMask
}, 
 232 static const le_int32 featureMapCount 
= LE_ARRAY_SIZE(featureMap
); 
 234 // The stateTable is used to calculate the end (the length) of a well 
 235 // formed Khmer Syllable.  
 237 // Each horizontal line is ordered exactly the same way as the values in KhmerClassTable 
 238 // CharClassValues in KhmerReordering.h This coincidence of values allows the 
 239 // follow up of the table. 
 241 // Each line corresponds to a state, which does not necessarily need to be a type 
 242 // of component... for example, state 2 is a base, with is always a first character 
 243 // in the syllable, but the state could be produced a consonant of any type when 
 244 // it is the first character that is analysed (in ground state). 
 246 // Differentiating 3 types of consonants is necessary in order to  
 247 // forbid the use of certain combinations, such as having a second 
 248 // coeng after a coeng RO,  
 249 // The inexistent possibility of having a type 3 after another type 3 is permitted, 
 250 // eliminating it would very much complicate the table, and it does not create typing 
 251 // problems, as the case above. 
 253 // The table is quite complex, in order to limit the number of coeng consonants 
 254 // to 2 (by means of the table). 
 256 // There a peculiarity, as far as Unicode is concerned: 
 257 // - The consonant-shifter is considered in two possible different  
 258 //   locations, the one considered in Unicode 3.0 and the one considered in 
 259 //   Unicode 4.0. (there is a backwards compatibility problem in this standard). 
 262 // xx    independent character, such as a number, punctuation sign or non-khmer char 
 264 // c1    Khmer consonant of type 1 or an independent vowel 
 265 //       that is, a letter in which the subscript for is only under the 
 266 //       base, not taking any space to the right or to the left 
 268 // c2    Khmer consonant of type 2, the coeng form takes space under 
 269 //       and to the left of the base (only RO is of this type) 
 271 // c3    Khmer consonant of type 3. Its subscript form takes space under 
 272 //       and to the right of the base.  
 274 // cs    Khmer consonant shifter          
 278 // co    coeng character (u17D2) 
 280 // dv    dependent vowel (including split vowels, they are treated in the same way). 
 281 //       even if dv is not defined above, the component that is really tested for is 
 282 //       KhmerClassTable::CC_DEPENDENT_VOWEL, which is common to all dependent vowels 
 284 // zwj   Zero Width joiner 
 286 // zwnj  Zero width non joiner   
 292 // there are lines with equal content but for an easier understanding  
 293 // (and maybe change in the future) we did not join them 
 295 static const le_int8 khmerStateTable
[][KhmerClassTable::CC_COUNT
] = 
 298 //   xx  c1  c2  c3 zwnj cs  rb  co  dv  sa  sp zwj   
 299     { 1,  2,  2,  2,  1,  1,  1,  6,  1,  1,  1,  2}, //  0 - ground state 
 300     {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, //  1 - exit state (or sign to the right of the syllable) 
 301     {-1, -1, -1, -1,  3,  4,  5,  6, 16, 17,  1, -1}, //  2 - Base consonant     
 302     {-1, -1, -1, -1, -1,  4, -1, -1, 16, -1, -1, -1}, //  3 - First ZWNJ before a register shifter 
 303                                                       //      It can only be followed by a shifter or a vowel 
 304     {-1, -1, -1, -1, 15, -1, -1,  6, 16, 17,  1, 14}, //  4 - First register shifter              
 305     {-1, -1, -1, -1, -1, -1, -1, -1, 20, -1,  1, -1}, //  5 - Robat 
 306     {-1,  7,  8,  9, -1, -1, -1, -1, -1, -1, -1, -1}, //  6 - First Coeng 
 307     {-1, -1, -1, -1, 12, 13, -1, 10, 16, 17,  1, 14}, //  7 - First consonant of type 1 after coeng  
 308     {-1, -1, -1, -1, 12, 13, -1, -1, 16, 17,  1, 14}, //  8 - First consonant of type 2 after coeng 
 309     {-1, -1, -1, -1, 12, 13, -1, 10, 16, 17,  1, 14}, //  9 - First consonant or type 3 after ceong 
 310     {-1, 11, 11, 11, -1, -1, -1, -1, -1, -1, -1, -1}, // 10 - Second Coeng (no register shifter before) 
 311     {-1, -1, -1, -1, 15, -1, -1, -1, 16, 17,  1, 14}, // 11 - Second coeng consonant (or ind. vowel) no register shifter before  
 312     {-1, -1, -1, -1, -1, 13, -1, -1, 16, -1, -1, -1}, // 12 - Second ZWNJ before a register shifter 
 313     {-1, -1, -1, -1, 15, -1, -1, -1, 16, 17,  1, 14}, // 13 - Second register shifter  
 314     {-1, -1, -1, -1, -1, -1, -1, -1, 16, -1, -1, -1}, // 14 - ZWJ before vowel     
 315     {-1, -1, -1, -1, -1, -1, -1, -1, 16, -1, -1, -1}, // 15 - ZWNJ before vowel 
 316     {-1, -1, -1, -1, -1, -1, -1, -1, -1, 17,  1, 18}, // 16 - dependent vowel  
 317     {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  1, 18}, // 17 - sign above 
 318     {-1, -1, -1, -1, -1, -1, -1, 19, -1, -1, -1, -1}, // 18 - ZWJ after vowel     
 319     {-1,  1, -1,  1, -1, -1, -1, -1, -1, -1, -1, -1}, // 19 - Third coeng  
 320     {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  1, -1}, // 20 - dependent vowel after a Robat 
 325 const FeatureMap 
*KhmerReordering::getFeatureMap(le_int32 
&count
) 
 327     count 
= featureMapCount
; 
 333 // Given an input string of characters and a location in which to start looking 
 334 // calculate, using the state table, which one is the last character of the syllable 
 335 // that starts in the starting position. 
 336 le_int32 
KhmerReordering::findSyllable(const KhmerClassTable 
*classTable
, const LEUnicode 
*chars
, le_int32 prev
, le_int32 charCount
) 
 338     le_int32 cursor 
= prev
; 
 341     while (cursor 
< charCount
) { 
 342         KhmerClassTable::CharClass charClass 
= (classTable
->getCharClass(chars
[cursor
]) & KhmerClassTable::CF_CLASS_MASK
); 
 344         state 
= khmerStateTable
[state
][charClass
]; 
 357 // This is the real reordering function as applied to the Khmer language 
 359 le_int32 
KhmerReordering::reorder(const LEUnicode 
*chars
, le_int32 charCount
, le_int32 
/*scriptCode*/, 
 360                                   LEUnicode 
*outChars
, LEGlyphStorage 
&glyphStorage
) 
 362     const KhmerClassTable 
*classTable 
= KhmerClassTable::getKhmerClassTable(); 
 364     KhmerReorderingOutput 
output(outChars
, glyphStorage
); 
 365     KhmerClassTable::CharClass charClass
; 
 366     le_int32 i
, prev 
= 0, coengRo
; 
 369     // This loop only exits when we reach the end of a run, which may contain  
 370     // several syllables. 
 371     while (prev 
< charCount
) { 
 372         le_int32 syllable 
= findSyllable(classTable
, chars
, prev
, charCount
); 
 376         // write a pre vowel or the pre part of a split vowel first 
 377         // and look out for coeng + ro. RO is the only vowel of type 2, and 
 378         // therefore the only one that requires saving space before the base. 
 379         coengRo 
= -1;  // There is no Coeng Ro, if found this value will change 
 380         for (i 
= prev
; i 
< syllable
; i 
+= 1) { 
 381             charClass 
= classTable
->getCharClass(chars
[i
]); 
 383             // if a split vowel, write the pre part. In Khmer the pre part 
 384             // is the same for all split vowels, same glyph as pre vowel C_VOWEL_E 
 385             if (charClass 
& KhmerClassTable::CF_SPLIT_VOWEL
) { 
 386                 output
.writeChar(C_VOWEL_E
, i
, tagPref
); 
 387                 break; // there can be only one vowel 
 390             // if a vowel with pos before write it out 
 391             if (charClass 
& KhmerClassTable::CF_POS_BEFORE
) { 
 392                 output
.writeChar(chars
[i
], i
, tagPref
); 
 393                 break; // there can be only one vowel  
 396             // look for coeng + ro and remember position 
 397             // works because coeng + ro is always in front of a vowel (if there is a vowel)  
 398             // and because CC_CONSONANT2 is enough to identify it, as it is the only consonant 
 400             if ( (charClass 
& KhmerClassTable::CF_COENG
) && (i 
+ 1 < syllable
) && 
 401                  ( (classTable
->getCharClass(chars
[i 
+ 1]) & KhmerClassTable::CF_CLASS_MASK
) == KhmerClassTable::CC_CONSONANT2
) ) 
 407         // write coeng + ro if found 
 409             output
.writeChar(C_COENG
, coengRo
, tagPref
); 
 410             output
.writeChar(C_RO
, coengRo 
+ 1, tagPref
); 
 413         // shall we add a dotted circle? 
 414         // If in the position in which the base should be (first char in the string) there is 
 415         // a character that has the Dotted circle flag (a character that cannot be a base) 
 416         // then write a dotted circle 
 417         if (classTable
->getCharClass(chars
[prev
]) & KhmerClassTable::CF_DOTTED_CIRCLE
) { 
 418             output
.writeChar(C_DOTTED_CIRCLE
, prev
, tagDefault
);         
 421         // copy what is left to the output, skipping before vowels and coeng Ro if they are present 
 422         for (i 
= prev
; i 
< syllable
; i 
+= 1) { 
 423             charClass 
= classTable
->getCharClass(chars
[i
]); 
 425             // skip a before vowel, it was already processed 
 426             if (charClass 
& KhmerClassTable::CF_POS_BEFORE
) { 
 430             // skip coeng + ro, it was already processed 
 436             switch (charClass 
& KhmerClassTable::CF_POS_MASK
) { 
 437                 case KhmerClassTable::CF_POS_ABOVE 
: 
 438                     output
.writeChar(chars
[i
], i
, tagAbvf
); 
 441                 case KhmerClassTable::CF_POS_AFTER 
: 
 442                     output
.writeChar(chars
[i
], i
, tagPstf
); 
 445                 case KhmerClassTable::CF_POS_BELOW 
: 
 446                     output
.writeChar(chars
[i
], i
, tagBlwf
); 
 450                     // assign the correct flags to a coeng consonant 
 451                     // Consonants of type 3 are taged as Post forms and those type 1 as below forms 
 452                     if ( (charClass 
& KhmerClassTable::CF_COENG
) && i 
+ 1 < syllable 
) { 
 453                         if ( (classTable
->getCharClass(chars
[i 
+ 1]) & KhmerClassTable::CF_CLASS_MASK
)  
 454                               == KhmerClassTable::CC_CONSONANT3
) { 
 455                             output
.writeChar(chars
[i
], i
, tagPstf
); 
 457                             output
.writeChar(chars
[i
], i
, tagPstf
); 
 460                             output
.writeChar(chars
[i
], i
, tagBlwf
); 
 462                             output
.writeChar(chars
[i
], i
, tagBlwf
); 
 466                     // if a shifter is followed by an above vowel change the shifter to below form, 
 467                     // an above vowel can have two possible positions i + 1 or i + 3  
 468                     // (position i+1 corresponds to unicode 3, position i+3 to Unicode 4) 
 469                     // and there is an extra rule for C_VOWEL_AA + C_SIGN_NIKAHIT also for two 
 470                     // different positions, right after the shifter or after a vowel (Unicode 4) 
 471                     if ( (charClass 
& KhmerClassTable::CF_SHIFTER
) && (i 
+ 1 < syllable
) ) { 
 472                         if ((classTable
->getCharClass(chars
[i 
+ 1]) & KhmerClassTable::CF_ABOVE_VOWEL
) 
 474                                 && ( (classTable
->getCharClass(chars
[i 
+ 1]) & KhmerClassTable::CF_CLASS_MASK
) == C_VOWEL_AA
) 
 475                                 && ( (classTable
->getCharClass(chars
[i 
+ 2]) & KhmerClassTable::CF_CLASS_MASK
) == C_SIGN_NIKAHIT
)) 
 476                             || (i 
+ 3 < syllable 
&& (classTable
->getCharClass(chars
[i 
+ 3]) & KhmerClassTable::CF_ABOVE_VOWEL
)) 
 478                                 && ( (classTable
->getCharClass(chars
[i 
+ 3]) & KhmerClassTable::CF_CLASS_MASK
) == C_VOWEL_AA
) 
 479                                 && ( (classTable
->getCharClass(chars
[i 
+ 4]) & KhmerClassTable::CF_CLASS_MASK
) == C_SIGN_NIKAHIT
) ) )  
 481                             output
.writeChar(chars
[i
], i
, tagBlwf
); 
 486                     // default - any other characters 
 487                     output
.writeChar(chars
[i
], i
, tagDefault
); 
 492         prev 
= syllable
; // move the pointer to the start of next syllable 
 495     return output
.getOutputIndex();