1*2d1272b8SAndroid Build Coastguard Worker /*
2*2d1272b8SAndroid Build Coastguard Worker * Copyright © 2013 Google, Inc.
3*2d1272b8SAndroid Build Coastguard Worker *
4*2d1272b8SAndroid Build Coastguard Worker * This is part of HarfBuzz, a text shaping library.
5*2d1272b8SAndroid Build Coastguard Worker *
6*2d1272b8SAndroid Build Coastguard Worker * Permission is hereby granted, without written agreement and without
7*2d1272b8SAndroid Build Coastguard Worker * license or royalty fees, to use, copy, modify, and distribute this
8*2d1272b8SAndroid Build Coastguard Worker * software and its documentation for any purpose, provided that the
9*2d1272b8SAndroid Build Coastguard Worker * above copyright notice and the following two paragraphs appear in
10*2d1272b8SAndroid Build Coastguard Worker * all copies of this software.
11*2d1272b8SAndroid Build Coastguard Worker *
12*2d1272b8SAndroid Build Coastguard Worker * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13*2d1272b8SAndroid Build Coastguard Worker * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14*2d1272b8SAndroid Build Coastguard Worker * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15*2d1272b8SAndroid Build Coastguard Worker * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
16*2d1272b8SAndroid Build Coastguard Worker * DAMAGE.
17*2d1272b8SAndroid Build Coastguard Worker *
18*2d1272b8SAndroid Build Coastguard Worker * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19*2d1272b8SAndroid Build Coastguard Worker * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20*2d1272b8SAndroid Build Coastguard Worker * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
21*2d1272b8SAndroid Build Coastguard Worker * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22*2d1272b8SAndroid Build Coastguard Worker * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
23*2d1272b8SAndroid Build Coastguard Worker *
24*2d1272b8SAndroid Build Coastguard Worker * Google Author(s): Behdad Esfahbod
25*2d1272b8SAndroid Build Coastguard Worker */
26*2d1272b8SAndroid Build Coastguard Worker
27*2d1272b8SAndroid Build Coastguard Worker #include "hb.hh"
28*2d1272b8SAndroid Build Coastguard Worker
29*2d1272b8SAndroid Build Coastguard Worker #ifndef HB_NO_OT_SHAPE
30*2d1272b8SAndroid Build Coastguard Worker
31*2d1272b8SAndroid Build Coastguard Worker #include "hb-ot-shaper.hh"
32*2d1272b8SAndroid Build Coastguard Worker
33*2d1272b8SAndroid Build Coastguard Worker
34*2d1272b8SAndroid Build Coastguard Worker /* Hangul shaper */
35*2d1272b8SAndroid Build Coastguard Worker
36*2d1272b8SAndroid Build Coastguard Worker
37*2d1272b8SAndroid Build Coastguard Worker /* Same order as the feature array below */
38*2d1272b8SAndroid Build Coastguard Worker enum {
39*2d1272b8SAndroid Build Coastguard Worker _JMO,
40*2d1272b8SAndroid Build Coastguard Worker
41*2d1272b8SAndroid Build Coastguard Worker LJMO,
42*2d1272b8SAndroid Build Coastguard Worker VJMO,
43*2d1272b8SAndroid Build Coastguard Worker TJMO,
44*2d1272b8SAndroid Build Coastguard Worker
45*2d1272b8SAndroid Build Coastguard Worker FIRST_HANGUL_FEATURE = LJMO,
46*2d1272b8SAndroid Build Coastguard Worker HANGUL_FEATURE_COUNT = TJMO + 1
47*2d1272b8SAndroid Build Coastguard Worker };
48*2d1272b8SAndroid Build Coastguard Worker
49*2d1272b8SAndroid Build Coastguard Worker static const hb_tag_t hangul_features[HANGUL_FEATURE_COUNT] =
50*2d1272b8SAndroid Build Coastguard Worker {
51*2d1272b8SAndroid Build Coastguard Worker HB_TAG_NONE,
52*2d1272b8SAndroid Build Coastguard Worker HB_TAG('l','j','m','o'),
53*2d1272b8SAndroid Build Coastguard Worker HB_TAG('v','j','m','o'),
54*2d1272b8SAndroid Build Coastguard Worker HB_TAG('t','j','m','o')
55*2d1272b8SAndroid Build Coastguard Worker };
56*2d1272b8SAndroid Build Coastguard Worker
57*2d1272b8SAndroid Build Coastguard Worker static void
collect_features_hangul(hb_ot_shape_planner_t * plan)58*2d1272b8SAndroid Build Coastguard Worker collect_features_hangul (hb_ot_shape_planner_t *plan)
59*2d1272b8SAndroid Build Coastguard Worker {
60*2d1272b8SAndroid Build Coastguard Worker hb_ot_map_builder_t *map = &plan->map;
61*2d1272b8SAndroid Build Coastguard Worker
62*2d1272b8SAndroid Build Coastguard Worker for (unsigned int i = FIRST_HANGUL_FEATURE; i < HANGUL_FEATURE_COUNT; i++)
63*2d1272b8SAndroid Build Coastguard Worker map->add_feature (hangul_features[i]);
64*2d1272b8SAndroid Build Coastguard Worker }
65*2d1272b8SAndroid Build Coastguard Worker
66*2d1272b8SAndroid Build Coastguard Worker static void
override_features_hangul(hb_ot_shape_planner_t * plan)67*2d1272b8SAndroid Build Coastguard Worker override_features_hangul (hb_ot_shape_planner_t *plan)
68*2d1272b8SAndroid Build Coastguard Worker {
69*2d1272b8SAndroid Build Coastguard Worker /* Uniscribe does not apply 'calt' for Hangul, and certain fonts
70*2d1272b8SAndroid Build Coastguard Worker * (Noto Sans CJK, Source Sans Han, etc) apply all of jamo lookups
71*2d1272b8SAndroid Build Coastguard Worker * in calt, which is not desirable. */
72*2d1272b8SAndroid Build Coastguard Worker plan->map.disable_feature (HB_TAG('c','a','l','t'));
73*2d1272b8SAndroid Build Coastguard Worker }
74*2d1272b8SAndroid Build Coastguard Worker
75*2d1272b8SAndroid Build Coastguard Worker struct hangul_shape_plan_t
76*2d1272b8SAndroid Build Coastguard Worker {
77*2d1272b8SAndroid Build Coastguard Worker hb_mask_t mask_array[HANGUL_FEATURE_COUNT];
78*2d1272b8SAndroid Build Coastguard Worker };
79*2d1272b8SAndroid Build Coastguard Worker
80*2d1272b8SAndroid Build Coastguard Worker static void *
data_create_hangul(const hb_ot_shape_plan_t * plan)81*2d1272b8SAndroid Build Coastguard Worker data_create_hangul (const hb_ot_shape_plan_t *plan)
82*2d1272b8SAndroid Build Coastguard Worker {
83*2d1272b8SAndroid Build Coastguard Worker hangul_shape_plan_t *hangul_plan = (hangul_shape_plan_t *) hb_calloc (1, sizeof (hangul_shape_plan_t));
84*2d1272b8SAndroid Build Coastguard Worker if (unlikely (!hangul_plan))
85*2d1272b8SAndroid Build Coastguard Worker return nullptr;
86*2d1272b8SAndroid Build Coastguard Worker
87*2d1272b8SAndroid Build Coastguard Worker for (unsigned int i = 0; i < HANGUL_FEATURE_COUNT; i++)
88*2d1272b8SAndroid Build Coastguard Worker hangul_plan->mask_array[i] = plan->map.get_1_mask (hangul_features[i]);
89*2d1272b8SAndroid Build Coastguard Worker
90*2d1272b8SAndroid Build Coastguard Worker return hangul_plan;
91*2d1272b8SAndroid Build Coastguard Worker }
92*2d1272b8SAndroid Build Coastguard Worker
93*2d1272b8SAndroid Build Coastguard Worker static void
data_destroy_hangul(void * data)94*2d1272b8SAndroid Build Coastguard Worker data_destroy_hangul (void *data)
95*2d1272b8SAndroid Build Coastguard Worker {
96*2d1272b8SAndroid Build Coastguard Worker hb_free (data);
97*2d1272b8SAndroid Build Coastguard Worker }
98*2d1272b8SAndroid Build Coastguard Worker
99*2d1272b8SAndroid Build Coastguard Worker /* Constants for algorithmic hangul syllable [de]composition. */
100*2d1272b8SAndroid Build Coastguard Worker #define LBase 0x1100u
101*2d1272b8SAndroid Build Coastguard Worker #define VBase 0x1161u
102*2d1272b8SAndroid Build Coastguard Worker #define TBase 0x11A7u
103*2d1272b8SAndroid Build Coastguard Worker #define LCount 19u
104*2d1272b8SAndroid Build Coastguard Worker #define VCount 21u
105*2d1272b8SAndroid Build Coastguard Worker #define TCount 28u
106*2d1272b8SAndroid Build Coastguard Worker #define SBase 0xAC00u
107*2d1272b8SAndroid Build Coastguard Worker #define NCount (VCount * TCount)
108*2d1272b8SAndroid Build Coastguard Worker #define SCount (LCount * NCount)
109*2d1272b8SAndroid Build Coastguard Worker
110*2d1272b8SAndroid Build Coastguard Worker #define isCombiningL(u) (hb_in_range<hb_codepoint_t> ((u), LBase, LBase+LCount-1))
111*2d1272b8SAndroid Build Coastguard Worker #define isCombiningV(u) (hb_in_range<hb_codepoint_t> ((u), VBase, VBase+VCount-1))
112*2d1272b8SAndroid Build Coastguard Worker #define isCombiningT(u) (hb_in_range<hb_codepoint_t> ((u), TBase+1, TBase+TCount-1))
113*2d1272b8SAndroid Build Coastguard Worker #define isCombinedS(u) (hb_in_range<hb_codepoint_t> ((u), SBase, SBase+SCount-1))
114*2d1272b8SAndroid Build Coastguard Worker
115*2d1272b8SAndroid Build Coastguard Worker #define isL(u) (hb_in_ranges<hb_codepoint_t> ((u), 0x1100u, 0x115Fu, 0xA960u, 0xA97Cu))
116*2d1272b8SAndroid Build Coastguard Worker #define isV(u) (hb_in_ranges<hb_codepoint_t> ((u), 0x1160u, 0x11A7u, 0xD7B0u, 0xD7C6u))
117*2d1272b8SAndroid Build Coastguard Worker #define isT(u) (hb_in_ranges<hb_codepoint_t> ((u), 0x11A8u, 0x11FFu, 0xD7CBu, 0xD7FBu))
118*2d1272b8SAndroid Build Coastguard Worker
119*2d1272b8SAndroid Build Coastguard Worker #define isHangulTone(u) (hb_in_range<hb_codepoint_t> ((u), 0x302Eu, 0x302Fu))
120*2d1272b8SAndroid Build Coastguard Worker
121*2d1272b8SAndroid Build Coastguard Worker /* buffer var allocations */
122*2d1272b8SAndroid Build Coastguard Worker #define hangul_shaping_feature() ot_shaper_var_u8_auxiliary() /* hangul jamo shaping feature */
123*2d1272b8SAndroid Build Coastguard Worker
124*2d1272b8SAndroid Build Coastguard Worker static bool
is_zero_width_char(hb_font_t * font,hb_codepoint_t unicode)125*2d1272b8SAndroid Build Coastguard Worker is_zero_width_char (hb_font_t *font,
126*2d1272b8SAndroid Build Coastguard Worker hb_codepoint_t unicode)
127*2d1272b8SAndroid Build Coastguard Worker {
128*2d1272b8SAndroid Build Coastguard Worker hb_codepoint_t glyph;
129*2d1272b8SAndroid Build Coastguard Worker return hb_font_get_glyph (font, unicode, 0, &glyph) && hb_font_get_glyph_h_advance (font, glyph) == 0;
130*2d1272b8SAndroid Build Coastguard Worker }
131*2d1272b8SAndroid Build Coastguard Worker
132*2d1272b8SAndroid Build Coastguard Worker static void
preprocess_text_hangul(const hb_ot_shape_plan_t * plan HB_UNUSED,hb_buffer_t * buffer,hb_font_t * font)133*2d1272b8SAndroid Build Coastguard Worker preprocess_text_hangul (const hb_ot_shape_plan_t *plan HB_UNUSED,
134*2d1272b8SAndroid Build Coastguard Worker hb_buffer_t *buffer,
135*2d1272b8SAndroid Build Coastguard Worker hb_font_t *font)
136*2d1272b8SAndroid Build Coastguard Worker {
137*2d1272b8SAndroid Build Coastguard Worker HB_BUFFER_ALLOCATE_VAR (buffer, hangul_shaping_feature);
138*2d1272b8SAndroid Build Coastguard Worker
139*2d1272b8SAndroid Build Coastguard Worker /* Hangul syllables come in two shapes: LV, and LVT. Of those:
140*2d1272b8SAndroid Build Coastguard Worker *
141*2d1272b8SAndroid Build Coastguard Worker * - LV can be precomposed, or decomposed. Lets call those
142*2d1272b8SAndroid Build Coastguard Worker * <LV> and <L,V>,
143*2d1272b8SAndroid Build Coastguard Worker * - LVT can be fully precomposed, partially precomposed, or
144*2d1272b8SAndroid Build Coastguard Worker * fully decomposed. Ie. <LVT>, <LV,T>, or <L,V,T>.
145*2d1272b8SAndroid Build Coastguard Worker *
146*2d1272b8SAndroid Build Coastguard Worker * The composition / decomposition is mechanical. However, not
147*2d1272b8SAndroid Build Coastguard Worker * all <L,V> sequences compose, and not all <LV,T> sequences
148*2d1272b8SAndroid Build Coastguard Worker * compose.
149*2d1272b8SAndroid Build Coastguard Worker *
150*2d1272b8SAndroid Build Coastguard Worker * Here are the specifics:
151*2d1272b8SAndroid Build Coastguard Worker *
152*2d1272b8SAndroid Build Coastguard Worker * - <L>: U+1100..115F, U+A960..A97F
153*2d1272b8SAndroid Build Coastguard Worker * - <V>: U+1160..11A7, U+D7B0..D7C7
154*2d1272b8SAndroid Build Coastguard Worker * - <T>: U+11A8..11FF, U+D7CB..D7FB
155*2d1272b8SAndroid Build Coastguard Worker *
156*2d1272b8SAndroid Build Coastguard Worker * - Only the <L,V> sequences for some of the U+11xx ranges combine.
157*2d1272b8SAndroid Build Coastguard Worker * - Only <LV,T> sequences for some of the Ts in U+11xx range combine.
158*2d1272b8SAndroid Build Coastguard Worker *
159*2d1272b8SAndroid Build Coastguard Worker * Here is what we want to accomplish in this shaper:
160*2d1272b8SAndroid Build Coastguard Worker *
161*2d1272b8SAndroid Build Coastguard Worker * - If the whole syllable can be precomposed, do that,
162*2d1272b8SAndroid Build Coastguard Worker * - Otherwise, fully decompose and apply ljmo/vjmo/tjmo features.
163*2d1272b8SAndroid Build Coastguard Worker * - If a valid syllable is followed by a Hangul tone mark, reorder the tone
164*2d1272b8SAndroid Build Coastguard Worker * mark to precede the whole syllable - unless it is a zero-width glyph, in
165*2d1272b8SAndroid Build Coastguard Worker * which case we leave it untouched, assuming it's designed to overstrike.
166*2d1272b8SAndroid Build Coastguard Worker *
167*2d1272b8SAndroid Build Coastguard Worker * That is, of the different possible syllables:
168*2d1272b8SAndroid Build Coastguard Worker *
169*2d1272b8SAndroid Build Coastguard Worker * <L>
170*2d1272b8SAndroid Build Coastguard Worker * <L,V>
171*2d1272b8SAndroid Build Coastguard Worker * <L,V,T>
172*2d1272b8SAndroid Build Coastguard Worker * <LV>
173*2d1272b8SAndroid Build Coastguard Worker * <LVT>
174*2d1272b8SAndroid Build Coastguard Worker * <LV, T>
175*2d1272b8SAndroid Build Coastguard Worker *
176*2d1272b8SAndroid Build Coastguard Worker * - <L> needs no work.
177*2d1272b8SAndroid Build Coastguard Worker *
178*2d1272b8SAndroid Build Coastguard Worker * - <LV> and <LVT> can stay the way they are if the font supports them, otherwise we
179*2d1272b8SAndroid Build Coastguard Worker * should fully decompose them if font supports.
180*2d1272b8SAndroid Build Coastguard Worker *
181*2d1272b8SAndroid Build Coastguard Worker * - <L,V> and <L,V,T> we should compose if the whole thing can be composed.
182*2d1272b8SAndroid Build Coastguard Worker *
183*2d1272b8SAndroid Build Coastguard Worker * - <LV,T> we should compose if the whole thing can be composed, otherwise we should
184*2d1272b8SAndroid Build Coastguard Worker * decompose.
185*2d1272b8SAndroid Build Coastguard Worker */
186*2d1272b8SAndroid Build Coastguard Worker
187*2d1272b8SAndroid Build Coastguard Worker buffer->clear_output ();
188*2d1272b8SAndroid Build Coastguard Worker unsigned int start = 0, end = 0; /* Extent of most recently seen syllable;
189*2d1272b8SAndroid Build Coastguard Worker * valid only if start < end
190*2d1272b8SAndroid Build Coastguard Worker */
191*2d1272b8SAndroid Build Coastguard Worker unsigned int count = buffer->len;
192*2d1272b8SAndroid Build Coastguard Worker
193*2d1272b8SAndroid Build Coastguard Worker for (buffer->idx = 0; buffer->idx < count && buffer->successful;)
194*2d1272b8SAndroid Build Coastguard Worker {
195*2d1272b8SAndroid Build Coastguard Worker hb_codepoint_t u = buffer->cur().codepoint;
196*2d1272b8SAndroid Build Coastguard Worker
197*2d1272b8SAndroid Build Coastguard Worker if (isHangulTone (u))
198*2d1272b8SAndroid Build Coastguard Worker {
199*2d1272b8SAndroid Build Coastguard Worker /*
200*2d1272b8SAndroid Build Coastguard Worker * We could cache the width of the tone marks and the existence of dotted-circle,
201*2d1272b8SAndroid Build Coastguard Worker * but the use of the Hangul tone mark characters seems to be rare enough that
202*2d1272b8SAndroid Build Coastguard Worker * I didn't bother for now.
203*2d1272b8SAndroid Build Coastguard Worker */
204*2d1272b8SAndroid Build Coastguard Worker if (start < end && end == buffer->out_len)
205*2d1272b8SAndroid Build Coastguard Worker {
206*2d1272b8SAndroid Build Coastguard Worker /* Tone mark follows a valid syllable; move it in front, unless it's zero width. */
207*2d1272b8SAndroid Build Coastguard Worker buffer->unsafe_to_break_from_outbuffer (start, buffer->idx);
208*2d1272b8SAndroid Build Coastguard Worker if (unlikely (!buffer->next_glyph ())) break;
209*2d1272b8SAndroid Build Coastguard Worker if (!is_zero_width_char (font, u))
210*2d1272b8SAndroid Build Coastguard Worker {
211*2d1272b8SAndroid Build Coastguard Worker buffer->merge_out_clusters (start, end + 1);
212*2d1272b8SAndroid Build Coastguard Worker hb_glyph_info_t *info = buffer->out_info;
213*2d1272b8SAndroid Build Coastguard Worker hb_glyph_info_t tone = info[end];
214*2d1272b8SAndroid Build Coastguard Worker memmove (&info[start + 1], &info[start], (end - start) * sizeof (hb_glyph_info_t));
215*2d1272b8SAndroid Build Coastguard Worker info[start] = tone;
216*2d1272b8SAndroid Build Coastguard Worker }
217*2d1272b8SAndroid Build Coastguard Worker }
218*2d1272b8SAndroid Build Coastguard Worker else
219*2d1272b8SAndroid Build Coastguard Worker {
220*2d1272b8SAndroid Build Coastguard Worker /* No valid syllable as base for tone mark; try to insert dotted circle. */
221*2d1272b8SAndroid Build Coastguard Worker if (!(buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE) &&
222*2d1272b8SAndroid Build Coastguard Worker font->has_glyph (0x25CCu))
223*2d1272b8SAndroid Build Coastguard Worker {
224*2d1272b8SAndroid Build Coastguard Worker hb_codepoint_t chars[2];
225*2d1272b8SAndroid Build Coastguard Worker if (!is_zero_width_char (font, u))
226*2d1272b8SAndroid Build Coastguard Worker {
227*2d1272b8SAndroid Build Coastguard Worker chars[0] = u;
228*2d1272b8SAndroid Build Coastguard Worker chars[1] = 0x25CCu;
229*2d1272b8SAndroid Build Coastguard Worker } else
230*2d1272b8SAndroid Build Coastguard Worker {
231*2d1272b8SAndroid Build Coastguard Worker chars[0] = 0x25CCu;
232*2d1272b8SAndroid Build Coastguard Worker chars[1] = u;
233*2d1272b8SAndroid Build Coastguard Worker }
234*2d1272b8SAndroid Build Coastguard Worker (void) buffer->replace_glyphs (1, 2, chars);
235*2d1272b8SAndroid Build Coastguard Worker }
236*2d1272b8SAndroid Build Coastguard Worker else
237*2d1272b8SAndroid Build Coastguard Worker {
238*2d1272b8SAndroid Build Coastguard Worker /* No dotted circle available in the font; just leave tone mark untouched. */
239*2d1272b8SAndroid Build Coastguard Worker (void) buffer->next_glyph ();
240*2d1272b8SAndroid Build Coastguard Worker }
241*2d1272b8SAndroid Build Coastguard Worker }
242*2d1272b8SAndroid Build Coastguard Worker start = end = buffer->out_len;
243*2d1272b8SAndroid Build Coastguard Worker continue;
244*2d1272b8SAndroid Build Coastguard Worker }
245*2d1272b8SAndroid Build Coastguard Worker
246*2d1272b8SAndroid Build Coastguard Worker start = buffer->out_len; /* Remember current position as a potential syllable start;
247*2d1272b8SAndroid Build Coastguard Worker * will only be used if we set end to a later position.
248*2d1272b8SAndroid Build Coastguard Worker */
249*2d1272b8SAndroid Build Coastguard Worker
250*2d1272b8SAndroid Build Coastguard Worker if (isL (u) && buffer->idx + 1 < count)
251*2d1272b8SAndroid Build Coastguard Worker {
252*2d1272b8SAndroid Build Coastguard Worker hb_codepoint_t l = u;
253*2d1272b8SAndroid Build Coastguard Worker hb_codepoint_t v = buffer->cur(+1).codepoint;
254*2d1272b8SAndroid Build Coastguard Worker if (isV (v))
255*2d1272b8SAndroid Build Coastguard Worker {
256*2d1272b8SAndroid Build Coastguard Worker /* Have <L,V> or <L,V,T>. */
257*2d1272b8SAndroid Build Coastguard Worker hb_codepoint_t t = 0;
258*2d1272b8SAndroid Build Coastguard Worker unsigned int tindex = 0;
259*2d1272b8SAndroid Build Coastguard Worker if (buffer->idx + 2 < count)
260*2d1272b8SAndroid Build Coastguard Worker {
261*2d1272b8SAndroid Build Coastguard Worker t = buffer->cur(+2).codepoint;
262*2d1272b8SAndroid Build Coastguard Worker if (isT (t))
263*2d1272b8SAndroid Build Coastguard Worker tindex = t - TBase; /* Only used if isCombiningT (t); otherwise invalid. */
264*2d1272b8SAndroid Build Coastguard Worker else
265*2d1272b8SAndroid Build Coastguard Worker t = 0; /* The next character was not a trailing jamo. */
266*2d1272b8SAndroid Build Coastguard Worker }
267*2d1272b8SAndroid Build Coastguard Worker buffer->unsafe_to_break (buffer->idx, buffer->idx + (t ? 3 : 2));
268*2d1272b8SAndroid Build Coastguard Worker
269*2d1272b8SAndroid Build Coastguard Worker /* We've got a syllable <L,V,T?>; see if it can potentially be composed. */
270*2d1272b8SAndroid Build Coastguard Worker if (isCombiningL (l) && isCombiningV (v) && (t == 0 || isCombiningT (t)))
271*2d1272b8SAndroid Build Coastguard Worker {
272*2d1272b8SAndroid Build Coastguard Worker /* Try to compose; if this succeeds, end is set to start+1. */
273*2d1272b8SAndroid Build Coastguard Worker hb_codepoint_t s = SBase + (l - LBase) * NCount + (v - VBase) * TCount + tindex;
274*2d1272b8SAndroid Build Coastguard Worker if (font->has_glyph (s))
275*2d1272b8SAndroid Build Coastguard Worker {
276*2d1272b8SAndroid Build Coastguard Worker (void) buffer->replace_glyphs (t ? 3 : 2, 1, &s);
277*2d1272b8SAndroid Build Coastguard Worker end = start + 1;
278*2d1272b8SAndroid Build Coastguard Worker continue;
279*2d1272b8SAndroid Build Coastguard Worker }
280*2d1272b8SAndroid Build Coastguard Worker }
281*2d1272b8SAndroid Build Coastguard Worker
282*2d1272b8SAndroid Build Coastguard Worker /* We didn't compose, either because it's an Old Hangul syllable without a
283*2d1272b8SAndroid Build Coastguard Worker * precomposed character in Unicode, or because the font didn't support the
284*2d1272b8SAndroid Build Coastguard Worker * necessary precomposed glyph.
285*2d1272b8SAndroid Build Coastguard Worker * Set jamo features on the individual glyphs, and advance past them.
286*2d1272b8SAndroid Build Coastguard Worker */
287*2d1272b8SAndroid Build Coastguard Worker buffer->cur().hangul_shaping_feature() = LJMO;
288*2d1272b8SAndroid Build Coastguard Worker (void) buffer->next_glyph ();
289*2d1272b8SAndroid Build Coastguard Worker buffer->cur().hangul_shaping_feature() = VJMO;
290*2d1272b8SAndroid Build Coastguard Worker (void) buffer->next_glyph ();
291*2d1272b8SAndroid Build Coastguard Worker if (t)
292*2d1272b8SAndroid Build Coastguard Worker {
293*2d1272b8SAndroid Build Coastguard Worker buffer->cur().hangul_shaping_feature() = TJMO;
294*2d1272b8SAndroid Build Coastguard Worker (void) buffer->next_glyph ();
295*2d1272b8SAndroid Build Coastguard Worker end = start + 3;
296*2d1272b8SAndroid Build Coastguard Worker }
297*2d1272b8SAndroid Build Coastguard Worker else
298*2d1272b8SAndroid Build Coastguard Worker end = start + 2;
299*2d1272b8SAndroid Build Coastguard Worker if (unlikely (!buffer->successful))
300*2d1272b8SAndroid Build Coastguard Worker break;
301*2d1272b8SAndroid Build Coastguard Worker if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES)
302*2d1272b8SAndroid Build Coastguard Worker buffer->merge_out_clusters (start, end);
303*2d1272b8SAndroid Build Coastguard Worker continue;
304*2d1272b8SAndroid Build Coastguard Worker }
305*2d1272b8SAndroid Build Coastguard Worker }
306*2d1272b8SAndroid Build Coastguard Worker
307*2d1272b8SAndroid Build Coastguard Worker else if (isCombinedS (u))
308*2d1272b8SAndroid Build Coastguard Worker {
309*2d1272b8SAndroid Build Coastguard Worker /* Have <LV>, <LVT>, or <LV,T> */
310*2d1272b8SAndroid Build Coastguard Worker hb_codepoint_t s = u;
311*2d1272b8SAndroid Build Coastguard Worker bool has_glyph = font->has_glyph (s);
312*2d1272b8SAndroid Build Coastguard Worker unsigned int lindex = (s - SBase) / NCount;
313*2d1272b8SAndroid Build Coastguard Worker unsigned int nindex = (s - SBase) % NCount;
314*2d1272b8SAndroid Build Coastguard Worker unsigned int vindex = nindex / TCount;
315*2d1272b8SAndroid Build Coastguard Worker unsigned int tindex = nindex % TCount;
316*2d1272b8SAndroid Build Coastguard Worker
317*2d1272b8SAndroid Build Coastguard Worker if (!tindex &&
318*2d1272b8SAndroid Build Coastguard Worker buffer->idx + 1 < count &&
319*2d1272b8SAndroid Build Coastguard Worker isCombiningT (buffer->cur(+1).codepoint))
320*2d1272b8SAndroid Build Coastguard Worker {
321*2d1272b8SAndroid Build Coastguard Worker /* <LV,T>, try to combine. */
322*2d1272b8SAndroid Build Coastguard Worker unsigned int new_tindex = buffer->cur(+1).codepoint - TBase;
323*2d1272b8SAndroid Build Coastguard Worker hb_codepoint_t new_s = s + new_tindex;
324*2d1272b8SAndroid Build Coastguard Worker if (font->has_glyph (new_s))
325*2d1272b8SAndroid Build Coastguard Worker {
326*2d1272b8SAndroid Build Coastguard Worker (void) buffer->replace_glyphs (2, 1, &new_s);
327*2d1272b8SAndroid Build Coastguard Worker end = start + 1;
328*2d1272b8SAndroid Build Coastguard Worker continue;
329*2d1272b8SAndroid Build Coastguard Worker }
330*2d1272b8SAndroid Build Coastguard Worker else
331*2d1272b8SAndroid Build Coastguard Worker buffer->unsafe_to_break (buffer->idx, buffer->idx + 2); /* Mark unsafe between LV and T. */
332*2d1272b8SAndroid Build Coastguard Worker }
333*2d1272b8SAndroid Build Coastguard Worker
334*2d1272b8SAndroid Build Coastguard Worker /* Otherwise, decompose if font doesn't support <LV> or <LVT>,
335*2d1272b8SAndroid Build Coastguard Worker * or if having non-combining <LV,T>. Note that we already handled
336*2d1272b8SAndroid Build Coastguard Worker * combining <LV,T> above. */
337*2d1272b8SAndroid Build Coastguard Worker if (!has_glyph ||
338*2d1272b8SAndroid Build Coastguard Worker (!tindex &&
339*2d1272b8SAndroid Build Coastguard Worker buffer->idx + 1 < count &&
340*2d1272b8SAndroid Build Coastguard Worker isT (buffer->cur(+1).codepoint)))
341*2d1272b8SAndroid Build Coastguard Worker {
342*2d1272b8SAndroid Build Coastguard Worker hb_codepoint_t decomposed[3] = {LBase + lindex,
343*2d1272b8SAndroid Build Coastguard Worker VBase + vindex,
344*2d1272b8SAndroid Build Coastguard Worker TBase + tindex};
345*2d1272b8SAndroid Build Coastguard Worker if (font->has_glyph (decomposed[0]) &&
346*2d1272b8SAndroid Build Coastguard Worker font->has_glyph (decomposed[1]) &&
347*2d1272b8SAndroid Build Coastguard Worker (!tindex || font->has_glyph (decomposed[2])))
348*2d1272b8SAndroid Build Coastguard Worker {
349*2d1272b8SAndroid Build Coastguard Worker unsigned int s_len = tindex ? 3 : 2;
350*2d1272b8SAndroid Build Coastguard Worker (void) buffer->replace_glyphs (1, s_len, decomposed);
351*2d1272b8SAndroid Build Coastguard Worker
352*2d1272b8SAndroid Build Coastguard Worker /* If we decomposed an LV because of a non-combining T following,
353*2d1272b8SAndroid Build Coastguard Worker * we want to include this T in the syllable.
354*2d1272b8SAndroid Build Coastguard Worker */
355*2d1272b8SAndroid Build Coastguard Worker if (has_glyph && !tindex)
356*2d1272b8SAndroid Build Coastguard Worker {
357*2d1272b8SAndroid Build Coastguard Worker (void) buffer->next_glyph ();
358*2d1272b8SAndroid Build Coastguard Worker s_len++;
359*2d1272b8SAndroid Build Coastguard Worker }
360*2d1272b8SAndroid Build Coastguard Worker if (unlikely (!buffer->successful))
361*2d1272b8SAndroid Build Coastguard Worker break;
362*2d1272b8SAndroid Build Coastguard Worker
363*2d1272b8SAndroid Build Coastguard Worker /* We decomposed S: apply jamo features to the individual glyphs
364*2d1272b8SAndroid Build Coastguard Worker * that are now in buffer->out_info.
365*2d1272b8SAndroid Build Coastguard Worker */
366*2d1272b8SAndroid Build Coastguard Worker hb_glyph_info_t *info = buffer->out_info;
367*2d1272b8SAndroid Build Coastguard Worker end = start + s_len;
368*2d1272b8SAndroid Build Coastguard Worker
369*2d1272b8SAndroid Build Coastguard Worker unsigned int i = start;
370*2d1272b8SAndroid Build Coastguard Worker info[i++].hangul_shaping_feature() = LJMO;
371*2d1272b8SAndroid Build Coastguard Worker info[i++].hangul_shaping_feature() = VJMO;
372*2d1272b8SAndroid Build Coastguard Worker if (i < end)
373*2d1272b8SAndroid Build Coastguard Worker info[i++].hangul_shaping_feature() = TJMO;
374*2d1272b8SAndroid Build Coastguard Worker
375*2d1272b8SAndroid Build Coastguard Worker if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES)
376*2d1272b8SAndroid Build Coastguard Worker buffer->merge_out_clusters (start, end);
377*2d1272b8SAndroid Build Coastguard Worker continue;
378*2d1272b8SAndroid Build Coastguard Worker }
379*2d1272b8SAndroid Build Coastguard Worker else if ((!tindex && buffer->idx + 1 < count && isT (buffer->cur(+1).codepoint)))
380*2d1272b8SAndroid Build Coastguard Worker buffer->unsafe_to_break (buffer->idx, buffer->idx + 2); /* Mark unsafe between LV and T. */
381*2d1272b8SAndroid Build Coastguard Worker }
382*2d1272b8SAndroid Build Coastguard Worker
383*2d1272b8SAndroid Build Coastguard Worker if (has_glyph)
384*2d1272b8SAndroid Build Coastguard Worker {
385*2d1272b8SAndroid Build Coastguard Worker /* We didn't decompose the S, so just advance past it and fall through. */
386*2d1272b8SAndroid Build Coastguard Worker end = start + 1;
387*2d1272b8SAndroid Build Coastguard Worker }
388*2d1272b8SAndroid Build Coastguard Worker }
389*2d1272b8SAndroid Build Coastguard Worker
390*2d1272b8SAndroid Build Coastguard Worker /* Didn't find a recognizable syllable, so we leave end <= start;
391*2d1272b8SAndroid Build Coastguard Worker * this will prevent tone-mark reordering happening.
392*2d1272b8SAndroid Build Coastguard Worker */
393*2d1272b8SAndroid Build Coastguard Worker (void) buffer->next_glyph ();
394*2d1272b8SAndroid Build Coastguard Worker }
395*2d1272b8SAndroid Build Coastguard Worker buffer->sync ();
396*2d1272b8SAndroid Build Coastguard Worker }
397*2d1272b8SAndroid Build Coastguard Worker
398*2d1272b8SAndroid Build Coastguard Worker static void
setup_masks_hangul(const hb_ot_shape_plan_t * plan,hb_buffer_t * buffer,hb_font_t * font HB_UNUSED)399*2d1272b8SAndroid Build Coastguard Worker setup_masks_hangul (const hb_ot_shape_plan_t *plan,
400*2d1272b8SAndroid Build Coastguard Worker hb_buffer_t *buffer,
401*2d1272b8SAndroid Build Coastguard Worker hb_font_t *font HB_UNUSED)
402*2d1272b8SAndroid Build Coastguard Worker {
403*2d1272b8SAndroid Build Coastguard Worker const hangul_shape_plan_t *hangul_plan = (const hangul_shape_plan_t *) plan->data;
404*2d1272b8SAndroid Build Coastguard Worker
405*2d1272b8SAndroid Build Coastguard Worker if (likely (hangul_plan))
406*2d1272b8SAndroid Build Coastguard Worker {
407*2d1272b8SAndroid Build Coastguard Worker unsigned int count = buffer->len;
408*2d1272b8SAndroid Build Coastguard Worker hb_glyph_info_t *info = buffer->info;
409*2d1272b8SAndroid Build Coastguard Worker for (unsigned int i = 0; i < count; i++, info++)
410*2d1272b8SAndroid Build Coastguard Worker info->mask |= hangul_plan->mask_array[info->hangul_shaping_feature()];
411*2d1272b8SAndroid Build Coastguard Worker }
412*2d1272b8SAndroid Build Coastguard Worker
413*2d1272b8SAndroid Build Coastguard Worker HB_BUFFER_DEALLOCATE_VAR (buffer, hangul_shaping_feature);
414*2d1272b8SAndroid Build Coastguard Worker }
415*2d1272b8SAndroid Build Coastguard Worker
416*2d1272b8SAndroid Build Coastguard Worker
417*2d1272b8SAndroid Build Coastguard Worker const hb_ot_shaper_t _hb_ot_shaper_hangul =
418*2d1272b8SAndroid Build Coastguard Worker {
419*2d1272b8SAndroid Build Coastguard Worker collect_features_hangul,
420*2d1272b8SAndroid Build Coastguard Worker override_features_hangul,
421*2d1272b8SAndroid Build Coastguard Worker data_create_hangul,
422*2d1272b8SAndroid Build Coastguard Worker data_destroy_hangul,
423*2d1272b8SAndroid Build Coastguard Worker preprocess_text_hangul,
424*2d1272b8SAndroid Build Coastguard Worker nullptr, /* postprocess_glyphs */
425*2d1272b8SAndroid Build Coastguard Worker nullptr, /* decompose */
426*2d1272b8SAndroid Build Coastguard Worker nullptr, /* compose */
427*2d1272b8SAndroid Build Coastguard Worker setup_masks_hangul,
428*2d1272b8SAndroid Build Coastguard Worker nullptr, /* reorder_marks */
429*2d1272b8SAndroid Build Coastguard Worker HB_TAG_NONE, /* gpos_tag */
430*2d1272b8SAndroid Build Coastguard Worker HB_OT_SHAPE_NORMALIZATION_MODE_NONE,
431*2d1272b8SAndroid Build Coastguard Worker HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE,
432*2d1272b8SAndroid Build Coastguard Worker false, /* fallback_position */
433*2d1272b8SAndroid Build Coastguard Worker };
434*2d1272b8SAndroid Build Coastguard Worker
435*2d1272b8SAndroid Build Coastguard Worker
436*2d1272b8SAndroid Build Coastguard Worker #endif
437