• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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