1 /* 2 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) 3 * 4 * This is part of HarfBuzz, an OpenType Layout engine library. 5 * 6 * Permission is hereby granted, without written agreement and without 7 * license or royalty fees, to use, copy, modify, and distribute this 8 * software and its documentation for any purpose, provided that the 9 * above copyright notice and the following two paragraphs appear in 10 * all copies of this software. 11 * 12 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR 13 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 14 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN 15 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 16 * DAMAGE. 17 * 18 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, 19 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 20 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS 21 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO 22 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 23 */ 24 25 #include "harfbuzz-shaper.h" 26 #include "harfbuzz-shaper-private.h" 27 28 #include <assert.h> 29 30 /* 31 // Hangul is a syllable based script. Unicode reserves a large range 32 // for precomposed hangul, where syllables are already precomposed to 33 // their final glyph shape. In addition, a so called jamo range is 34 // defined, that can be used to express old Hangul. Modern hangul 35 // syllables can also be expressed as jamo, and should be composed 36 // into syllables. The operation is rather simple and mathematical. 37 38 // Every hangul jamo is classified as being either a Leading consonant 39 // (L), and intermediat Vowel (V) or a trailing consonant (T). Modern 40 // hangul syllables (the ones in the precomposed area can be of type 41 // LV or LVT. 42 // 43 // Syllable breaks do _not_ occur between: 44 // 45 // L L, V or precomposed 46 // V, LV V, T 47 // LVT, T T 48 // 49 // A standard syllable is of the form L+V+T*. The above rules allow 50 // nonstandard syllables L*V*T*. To transform them into standard 51 // syllables fill characters L_f and V_f can be inserted. 52 */ 53 54 enum { 55 Hangul_SBase = 0xac00, 56 Hangul_LBase = 0x1100, 57 Hangul_VBase = 0x1161, 58 Hangul_TBase = 0x11a7, 59 Hangul_SCount = 11172, 60 Hangul_LCount = 19, 61 Hangul_VCount = 21, 62 Hangul_TCount = 28, 63 Hangul_NCount = 21*28 64 }; 65 66 #define hangul_isPrecomposed(uc) \ 67 (uc >= Hangul_SBase && uc < Hangul_SBase + Hangul_SCount) 68 69 #define hangul_isLV(uc) \ 70 ((uc - Hangul_SBase) % Hangul_TCount == 0) 71 72 typedef enum { 73 L, 74 V, 75 T, 76 LV, 77 LVT, 78 X 79 } HangulType; 80 81 static HangulType hangul_type(unsigned short uc) { 82 if (uc > Hangul_SBase && uc < Hangul_SBase + Hangul_SCount) 83 return hangul_isLV(uc) ? LV : LVT; 84 if (uc < Hangul_LBase || uc > 0x11ff) 85 return X; 86 if (uc < Hangul_VBase) 87 return L; 88 if (uc < Hangul_TBase) 89 return V; 90 return T; 91 } 92 93 static int hangul_nextSyllableBoundary(const HB_UChar16 *s, int start, int end) 94 { 95 const HB_UChar16 *uc = s + start; 96 97 HangulType state = hangul_type(*uc); 98 int pos = 1; 99 100 while (pos < end - start) { 101 HangulType newState = hangul_type(uc[pos]); 102 switch(newState) { 103 case X: 104 goto finish; 105 case L: 106 case V: 107 case T: 108 if (state > newState) 109 goto finish; 110 state = newState; 111 break; 112 case LV: 113 if (state > L) 114 goto finish; 115 state = V; 116 break; 117 case LVT: 118 if (state > L) 119 goto finish; 120 state = T; 121 } 122 ++pos; 123 } 124 125 finish: 126 return start+pos; 127 } 128 129 #ifndef NO_OPENTYPE 130 static const HB_OpenTypeFeature hangul_features [] = { 131 { HB_MAKE_TAG('c', 'c', 'm', 'p'), CcmpProperty }, 132 { HB_MAKE_TAG('l', 'j', 'm', 'o'), CcmpProperty }, 133 { HB_MAKE_TAG('v', 'j', 'm', 'o'), CcmpProperty }, 134 { HB_MAKE_TAG('t', 'j', 'm', 'o'), CcmpProperty }, 135 { 0, 0 } 136 }; 137 #endif 138 139 static HB_Bool hangul_shape_syllable(HB_ShaperItem *item, HB_Bool openType) 140 { 141 const HB_UChar16 *ch = item->string + item->item.pos; 142 int len = item->item.length; 143 #ifndef NO_OPENTYPE 144 const int availableGlyphs = item->num_glyphs; 145 #endif 146 147 int i; 148 HB_UChar16 composed = 0; 149 /* see if we can compose the syllable into a modern hangul */ 150 if (item->item.length == 2) { 151 int LIndex = ch[0] - Hangul_LBase; 152 int VIndex = ch[1] - Hangul_VBase; 153 if (LIndex >= 0 && LIndex < Hangul_LCount && 154 VIndex >= 0 && VIndex < Hangul_VCount) 155 composed = (LIndex * Hangul_VCount + VIndex) * Hangul_TCount + Hangul_SBase; 156 } else if (item->item.length == 3) { 157 int LIndex = ch[0] - Hangul_LBase; 158 int VIndex = ch[1] - Hangul_VBase; 159 int TIndex = ch[2] - Hangul_TBase; 160 if (LIndex >= 0 && LIndex < Hangul_LCount && 161 VIndex >= 0 && VIndex < Hangul_VCount && 162 TIndex >= 0 && TIndex < Hangul_TCount) 163 composed = (LIndex * Hangul_VCount + VIndex) * Hangul_TCount + TIndex + Hangul_SBase; 164 } 165 166 167 168 /* if we have a modern hangul use the composed form */ 169 if (composed) { 170 ch = &composed; 171 len = 1; 172 } 173 174 if (!item->font->klass->convertStringToGlyphIndices(item->font, 175 ch, len, 176 item->glyphs, &item->num_glyphs, 177 item->item.bidiLevel % 2)) 178 return FALSE; 179 for (i = 0; i < len; i++) { 180 item->attributes[i].mark = FALSE; 181 item->attributes[i].clusterStart = FALSE; 182 item->attributes[i].justification = 0; 183 item->attributes[i].zeroWidth = FALSE; 184 /*IDEBUG(" %d: %4x", i, ch[i].unicode()); */ 185 } 186 187 #ifndef NO_OPENTYPE 188 if (!composed && openType) { 189 HB_Bool positioned; 190 191 HB_STACKARRAY(unsigned short, logClusters, len); 192 for (i = 0; i < len; ++i) 193 logClusters[i] = i; 194 item->log_clusters = logClusters; 195 196 HB_OpenTypeShape(item, /*properties*/0); 197 198 positioned = HB_OpenTypePosition(item, availableGlyphs, /*doLogClusters*/FALSE); 199 200 HB_FREE_STACKARRAY(logClusters); 201 202 if (!positioned) 203 return FALSE; 204 } else { 205 HB_HeuristicPosition(item); 206 } 207 #endif 208 209 item->attributes[0].clusterStart = TRUE; 210 return TRUE; 211 } 212 213 HB_Bool HB_HangulShape(HB_ShaperItem *item) 214 { 215 const HB_UChar16 *uc = item->string + item->item.pos; 216 HB_Bool allPrecomposed = TRUE; 217 int i; 218 219 assert(item->item.script == HB_Script_Hangul); 220 221 for (i = 0; i < (int)item->item.length; ++i) { 222 if (!hangul_isPrecomposed(uc[i])) { 223 allPrecomposed = FALSE; 224 break; 225 } 226 } 227 228 if (!allPrecomposed) { 229 HB_Bool openType = FALSE; 230 unsigned short *logClusters = item->log_clusters; 231 HB_ShaperItem syllable; 232 int first_glyph = 0; 233 int sstart = item->item.pos; 234 int end = sstart + item->item.length; 235 236 #ifndef NO_OPENTYPE 237 openType = HB_SelectScript(item, hangul_features); 238 #endif 239 syllable = *item; 240 241 while (sstart < end) { 242 int send = hangul_nextSyllableBoundary(item->string, sstart, end); 243 244 syllable.item.pos = sstart; 245 syllable.item.length = send-sstart; 246 syllable.glyphs = item->glyphs + first_glyph; 247 syllable.attributes = item->attributes + first_glyph; 248 syllable.offsets = item->offsets + first_glyph; 249 syllable.advances = item->advances + first_glyph; 250 syllable.num_glyphs = item->num_glyphs - first_glyph; 251 if (!hangul_shape_syllable(&syllable, openType)) { 252 item->num_glyphs += syllable.num_glyphs; 253 return FALSE; 254 } 255 /* fix logcluster array */ 256 for (i = sstart; i < send; ++i) 257 logClusters[i-item->item.pos] = first_glyph; 258 sstart = send; 259 first_glyph += syllable.num_glyphs; 260 } 261 item->num_glyphs = first_glyph; 262 return TRUE; 263 } 264 265 return HB_BasicShape(item); 266 } 267 268 269