1 /* 2 * HangulLayoutEngine.cpp: OpenType processing for Han fonts. 3 * 4 * (C) Copyright IBM Corp. 1998-2008 - All Rights Reserved. 5 */ 6 7 #include "LETypes.h" 8 #include "LEScripts.h" 9 #include "LELanguages.h" 10 11 #include "LayoutEngine.h" 12 #include "OpenTypeLayoutEngine.h" 13 #include "HangulLayoutEngine.h" 14 #include "ScriptAndLanguageTags.h" 15 #include "LEGlyphStorage.h" 16 #include "OpenTypeTables.h" 17 18 U_NAMESPACE_BEGIN 19 20 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(HangulOpenTypeLayoutEngine) 21 22 23 #define FEATURE_MAP(name) {name ## FeatureTag, name ## FeatureMask} 24 25 #define LJMO_FIRST 0x1100 26 #define LJMO_LAST 0x1159 27 #define LJMO_FILL 0x115F 28 #define LJMO_COUNT 19 29 30 #define VJMO_FIRST 0x1161 31 #define VJMO_LAST 0x11A2 32 #define VJMO_FILL 0x1160 33 #define VJMO_COUNT 21 34 35 #define TJMO_FIRST 0x11A7 36 #define TJMO_LAST 0x11F9 37 #define TJMO_COUNT 28 38 39 #define HSYL_FIRST 0xAC00 40 #define HSYL_COUNT 11172 41 #define HSYL_LVCNT (VJMO_COUNT * TJMO_COUNT) 42 43 // Character classes 44 enum 45 { 46 CC_L = 0, 47 CC_V, 48 CC_T, 49 CC_LV, 50 CC_LVT, 51 CC_X, 52 CC_COUNT 53 }; 54 55 // Action flags 56 #define AF_L 1 57 #define AF_V 2 58 #define AF_T 4 59 60 // Actions 61 #define a_N 0 62 #define a_L (AF_L) 63 #define a_V (AF_V) 64 #define a_T (AF_T) 65 #define a_VT (AF_V | AF_T) 66 #define a_LV (AF_L | AF_V) 67 #define a_LVT (AF_L | AF_V | AF_T) 68 69 typedef struct 70 { 71 int32_t newState; 72 int32_t actionFlags; 73 } StateTransition; 74 75 static const StateTransition stateTable[][CC_COUNT] = 76 { 77 // L V T LV LVT X 78 { {1, a_L}, {2, a_LV}, {3, a_LVT}, {2, a_LV}, {3, a_LVT}, {4, a_T}}, // 0 - start 79 { {1, a_L}, {2, a_V}, {3, a_VT}, {2, a_LV}, {3, a_LVT}, {-1, a_V}}, // 1 - L+ 80 {{-1, a_N}, {2, a_V}, {3, a_T}, {-1, a_N}, {-1, a_N}, {-1, a_N}}, // 2 - L+V+ 81 {{-1, a_N}, {-1, a_N}, {3, a_T}, {-1, a_N}, {-1, a_N}, {-1, a_N}}, // 3 - L+V+T* 82 {{-1, a_N}, {-1, a_N}, {-1, a_N}, {-1, a_N}, {-1, a_N}, {4, a_T}} // 4 - X+ 83 }; 84 85 86 #define ccmpFeatureTag LE_CCMP_FEATURE_TAG 87 #define ljmoFeatureTag LE_LJMO_FEATURE_TAG 88 #define vjmoFeatureTag LE_VJMO_FEATURE_TAG 89 #define tjmoFeatureTag LE_TJMO_FEATURE_TAG 90 91 #define ccmpFeatureMask 0x80000000UL 92 #define ljmoFeatureMask 0x40000000UL 93 #define vjmoFeatureMask 0x20000000UL 94 #define tjmoFeatureMask 0x10000000UL 95 96 static const FeatureMap featureMap[] = 97 { 98 {ccmpFeatureTag, ccmpFeatureMask}, 99 {ljmoFeatureTag, ljmoFeatureMask}, 100 {vjmoFeatureTag, vjmoFeatureMask}, 101 {tjmoFeatureTag, tjmoFeatureMask} 102 }; 103 104 static const le_int32 featureMapCount = LE_ARRAY_SIZE(featureMap); 105 106 #define nullFeatures 0 107 #define ljmoFeatures (ccmpFeatureMask | ljmoFeatureMask) 108 #define vjmoFeatures (ccmpFeatureMask | vjmoFeatureMask | ljmoFeatureMask | tjmoFeatureMask) 109 #define tjmoFeatures (ccmpFeatureMask | tjmoFeatureMask | ljmoFeatureMask | vjmoFeatureMask) 110 111 static le_int32 compose(LEUnicode lead, LEUnicode vowel, LEUnicode trail, LEUnicode &syllable) 112 { 113 le_int32 lIndex = lead - LJMO_FIRST; 114 le_int32 vIndex = vowel - VJMO_FIRST; 115 le_int32 tIndex = trail - TJMO_FIRST; 116 le_int32 result = 3; 117 118 if ((lIndex < 0 || lIndex >= LJMO_COUNT ) || (vIndex < 0 || vIndex >= VJMO_COUNT)) { 119 return 0; 120 } 121 122 if (tIndex <= 0 || tIndex >= TJMO_COUNT) { 123 tIndex = 0; 124 result = 2; 125 } 126 127 syllable = (LEUnicode) ((lIndex * VJMO_COUNT + vIndex) * TJMO_COUNT + tIndex + HSYL_FIRST); 128 129 return result; 130 } 131 132 static le_int32 decompose(LEUnicode syllable, LEUnicode &lead, LEUnicode &vowel, LEUnicode &trail) 133 { 134 le_int32 sIndex = syllable - HSYL_FIRST; 135 136 if (sIndex < 0 || sIndex >= HSYL_COUNT) { 137 return 0; 138 } 139 140 lead = LJMO_FIRST + (sIndex / HSYL_LVCNT); 141 vowel = VJMO_FIRST + (sIndex % HSYL_LVCNT) / TJMO_COUNT; 142 trail = TJMO_FIRST + (sIndex % TJMO_COUNT); 143 144 if (trail == TJMO_FIRST) { 145 return 2; 146 } 147 148 return 3; 149 } 150 151 static le_int32 getCharClass(LEUnicode ch, LEUnicode &lead, LEUnicode &vowel, LEUnicode &trail) 152 { 153 lead = LJMO_FILL; 154 vowel = VJMO_FILL; 155 trail = TJMO_FIRST; 156 157 if (ch >= LJMO_FIRST && ch <= LJMO_LAST) { 158 lead = ch; 159 return CC_L; 160 } 161 162 if (ch >= VJMO_FIRST && ch <= VJMO_LAST) { 163 vowel = ch; 164 return CC_V; 165 } 166 167 if (ch > TJMO_FIRST && ch <= TJMO_LAST) { 168 trail = ch; 169 return CC_T; 170 } 171 172 le_int32 c = decompose(ch, lead, vowel, trail); 173 174 if (c == 2) { 175 return CC_LV; 176 } 177 178 if (c == 3) { 179 return CC_LVT; 180 } 181 182 trail = ch; 183 return CC_X; 184 } 185 186 HangulOpenTypeLayoutEngine::HangulOpenTypeLayoutEngine(const LEFontInstance *fontInstance, le_int32 scriptCode, le_int32 /*languageCode*/, 187 le_int32 typoFlags, const GlyphSubstitutionTableHeader *gsubTable, LEErrorCode &success) 188 : OpenTypeLayoutEngine(fontInstance, scriptCode, korLanguageCode, typoFlags, gsubTable, success) 189 { 190 fFeatureMap = featureMap; 191 fFeatureMapCount = featureMapCount; 192 fFeatureOrder = TRUE; 193 } 194 195 HangulOpenTypeLayoutEngine::HangulOpenTypeLayoutEngine(const LEFontInstance *fontInstance, le_int32 scriptCode, le_int32 /*languageCode*/, 196 le_int32 typoFlags, LEErrorCode &success) 197 : OpenTypeLayoutEngine(fontInstance, scriptCode, korLanguageCode, typoFlags, success) 198 { 199 fFeatureMap = featureMap; 200 fFeatureMapCount = featureMapCount; 201 fFeatureOrder = TRUE; 202 } 203 204 HangulOpenTypeLayoutEngine::~HangulOpenTypeLayoutEngine() 205 { 206 // nothing to do 207 } 208 209 le_int32 HangulOpenTypeLayoutEngine::characterProcessing(const LEUnicode chars[], le_int32 offset, le_int32 count, le_int32 max, le_bool rightToLeft, 210 LEUnicode *&outChars, LEGlyphStorage &glyphStorage, LEErrorCode &success) 211 { 212 if (LE_FAILURE(success)) { 213 return 0; 214 } 215 216 if (chars == NULL || offset < 0 || count < 0 || max < 0 || offset >= max || offset + count > max) { 217 success = LE_ILLEGAL_ARGUMENT_ERROR; 218 return 0; 219 } 220 221 le_int32 worstCase = count * 3; 222 223 outChars = LE_NEW_ARRAY(LEUnicode, worstCase); 224 225 if (outChars == NULL) { 226 success = LE_MEMORY_ALLOCATION_ERROR; 227 return 0; 228 } 229 230 glyphStorage.allocateGlyphArray(worstCase, rightToLeft, success); 231 glyphStorage.allocateAuxData(success); 232 233 if (LE_FAILURE(success)) { 234 LE_DELETE_ARRAY(outChars); 235 return 0; 236 } 237 238 le_int32 outCharCount = 0; 239 le_int32 limit = offset + count; 240 le_int32 i = offset; 241 242 while (i < limit) { 243 le_int32 state = 0; 244 le_int32 inStart = i; 245 le_int32 outStart = outCharCount; 246 247 while( i < limit) { 248 LEUnicode lead = 0; 249 LEUnicode vowel = 0; 250 LEUnicode trail = 0; 251 int32_t chClass = getCharClass(chars[i], lead, vowel, trail); 252 const StateTransition transition = stateTable[state][chClass]; 253 254 if (chClass == CC_X) { 255 /* Any character of type X will be stored as a trail jamo */ 256 if ((transition.actionFlags & AF_T) != 0) { 257 outChars[outCharCount] = trail; 258 glyphStorage.setCharIndex(outCharCount, i-offset, success); 259 glyphStorage.setAuxData(outCharCount++, nullFeatures, success); 260 } 261 } else { 262 /* Any Hangul will be fully decomposed. Output the decomposed characters. */ 263 if ((transition.actionFlags & AF_L) != 0) { 264 outChars[outCharCount] = lead; 265 glyphStorage.setCharIndex(outCharCount, i-offset, success); 266 glyphStorage.setAuxData(outCharCount++, ljmoFeatures, success); 267 } 268 269 if ((transition.actionFlags & AF_V) != 0) { 270 outChars[outCharCount] = vowel; 271 glyphStorage.setCharIndex(outCharCount, i-offset, success); 272 glyphStorage.setAuxData(outCharCount++, vjmoFeatures, success); 273 } 274 275 if ((transition.actionFlags & AF_T) != 0) { 276 outChars[outCharCount] = trail; 277 glyphStorage.setCharIndex(outCharCount, i-offset, success); 278 glyphStorage.setAuxData(outCharCount++, tjmoFeatures, success); 279 } 280 } 281 282 state = transition.newState; 283 284 /* Negative next state means stop. */ 285 if (state < 0) { 286 break; 287 } 288 289 i += 1; 290 } 291 292 le_int32 inLength = i - inStart; 293 le_int32 outLength = outCharCount - outStart; 294 295 /* 296 * See if the syllable can be composed into a single character. There are 5 297 * possible cases: 298 * 299 * Input Decomposed to Compose to 300 * LV L, V LV 301 * LVT L, V, T LVT 302 * L, V L, V LV, DEL 303 * LV, T L, V, T LVT, DEL 304 * L, V, T L, V, T LVT, DEL, DEL 305 */ 306 if ((inLength >= 1 && inLength <= 3) && (outLength == 2 || outLength == 3)) { 307 LEUnicode syllable = 0x0000; 308 LEUnicode lead = outChars[outStart]; 309 LEUnicode vowel = outChars[outStart + 1]; 310 LEUnicode trail = outLength == 3? outChars[outStart + 2] : TJMO_FIRST; 311 312 /* 313 * If the composition consumes the whole decomposed syllable, 314 * we can use it. 315 */ 316 if (compose(lead, vowel, trail, syllable) == outLength) { 317 outCharCount = outStart; 318 outChars[outCharCount] = syllable; 319 glyphStorage.setCharIndex(outCharCount, inStart-offset, success); 320 glyphStorage.setAuxData(outCharCount++, nullFeatures, success); 321 322 /* 323 * Replace the rest of the input characters with DEL. 324 */ 325 for(le_int32 d = inStart + 1; d < i; d += 1) { 326 outChars[outCharCount] = 0xFFFF; 327 glyphStorage.setCharIndex(outCharCount, d - offset, success); 328 glyphStorage.setAuxData(outCharCount++, nullFeatures, success); 329 } 330 } 331 } 332 } 333 334 glyphStorage.adoptGlyphCount(outCharCount); 335 return outCharCount; 336 } 337 338 U_NAMESPACE_END 339