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
hangul_type(unsigned short uc)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
hangul_nextSyllableBoundary(const HB_UChar16 * s,int start,int end)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
hangul_shape_syllable(HB_ShaperItem * item,HB_Bool openType)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
HB_HangulShape(HB_ShaperItem * item)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