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