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