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