| 1 | /* | 
| 2 |  * Copyright © 2015  Mozilla Foundation. | 
| 3 |  * Copyright © 2015  Google, Inc. | 
| 4 |  * | 
| 5 |  *  This is part of HarfBuzz, a text shaping library. | 
| 6 |  * | 
| 7 |  * Permission is hereby granted, without written agreement and without | 
| 8 |  * license or royalty fees, to use, copy, modify, and distribute this | 
| 9 |  * software and its documentation for any purpose, provided that the | 
| 10 |  * above copyright notice and the following two paragraphs appear in | 
| 11 |  * all copies of this software. | 
| 12 |  * | 
| 13 |  * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR | 
| 14 |  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES | 
| 15 |  * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN | 
| 16 |  * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH | 
| 17 |  * DAMAGE. | 
| 18 |  * | 
| 19 |  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, | 
| 20 |  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND | 
| 21 |  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS | 
| 22 |  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO | 
| 23 |  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. | 
| 24 |  * | 
| 25 |  * Mozilla Author(s): Jonathan Kew | 
| 26 |  * Google Author(s): Behdad Esfahbod | 
| 27 |  */ | 
| 28 |  | 
| 29 | #include "hb.hh" | 
| 30 |  | 
| 31 | #ifndef HB_NO_OT_SHAPE | 
| 32 |  | 
| 33 | #include "hb-ot-shape-complex-use.hh" | 
| 34 | #include "hb-ot-shape-complex-arabic.hh" | 
| 35 | #include "hb-ot-shape-complex-vowel-constraints.hh" | 
| 36 |  | 
| 37 | /* buffer var allocations */ | 
| 38 | #define use_category() complex_var_u8_0() | 
| 39 |  | 
| 40 |  | 
| 41 | /* | 
| 42 |  * Universal Shaping Engine. | 
| 43 |  * https://docs.microsoft.com/en-us/typography/script-development/use | 
| 44 |  */ | 
| 45 |  | 
| 46 | static const hb_tag_t | 
| 47 | use_basic_features[] = | 
| 48 | { | 
| 49 |   /* | 
| 50 |    * Basic features. | 
| 51 |    * These features are applied all at once, before reordering. | 
| 52 |    */ | 
| 53 |   HB_TAG('r','k','r','f'), | 
| 54 |   HB_TAG('a','b','v','f'), | 
| 55 |   HB_TAG('b','l','w','f'), | 
| 56 |   HB_TAG('h','a','l','f'), | 
| 57 |   HB_TAG('p','s','t','f'), | 
| 58 |   HB_TAG('v','a','t','u'), | 
| 59 |   HB_TAG('c','j','c','t'), | 
| 60 | }; | 
| 61 | static const hb_tag_t | 
| 62 | use_topographical_features[] = | 
| 63 | { | 
| 64 |   HB_TAG('i','s','o','l'), | 
| 65 |   HB_TAG('i','n','i','t'), | 
| 66 |   HB_TAG('m','e','d','i'), | 
| 67 |   HB_TAG('f','i','n','a'), | 
| 68 | }; | 
| 69 | /* Same order as use_topographical_features. */ | 
| 70 | enum joining_form_t { | 
| 71 |   USE_ISOL, | 
| 72 |   USE_INIT, | 
| 73 |   USE_MEDI, | 
| 74 |   USE_FINA, | 
| 75 |   _USE_NONE | 
| 76 | }; | 
| 77 | static const hb_tag_t | 
| 78 | use_other_features[] = | 
| 79 | { | 
| 80 |   /* | 
| 81 |    * Other features. | 
| 82 |    * These features are applied all at once, after reordering and | 
| 83 |    * clearing syllables. | 
| 84 |    */ | 
| 85 |   HB_TAG('a','b','v','s'), | 
| 86 |   HB_TAG('b','l','w','s'), | 
| 87 |   HB_TAG('h','a','l','n'), | 
| 88 |   HB_TAG('p','r','e','s'), | 
| 89 |   HB_TAG('p','s','t','s'), | 
| 90 | }; | 
| 91 |  | 
| 92 | static void | 
| 93 | setup_syllables_use (const hb_ot_shape_plan_t *plan, | 
| 94 | 		     hb_font_t *font, | 
| 95 | 		     hb_buffer_t *buffer); | 
| 96 | static void | 
| 97 | record_rphf_use (const hb_ot_shape_plan_t *plan, | 
| 98 | 		 hb_font_t *font, | 
| 99 | 		 hb_buffer_t *buffer); | 
| 100 | static void | 
| 101 | record_pref_use (const hb_ot_shape_plan_t *plan, | 
| 102 | 		 hb_font_t *font, | 
| 103 | 		 hb_buffer_t *buffer); | 
| 104 | static void | 
| 105 | reorder_use (const hb_ot_shape_plan_t *plan, | 
| 106 | 	     hb_font_t *font, | 
| 107 | 	     hb_buffer_t *buffer); | 
| 108 |  | 
| 109 | static void | 
| 110 | collect_features_use (hb_ot_shape_planner_t *plan) | 
| 111 | { | 
| 112 |   hb_ot_map_builder_t *map = &plan->map; | 
| 113 |  | 
| 114 |   /* Do this before any lookups have been applied. */ | 
| 115 |   map->add_gsub_pause (setup_syllables_use); | 
| 116 |  | 
| 117 |   /* "Default glyph pre-processing group" */ | 
| 118 |   map->enable_feature (HB_TAG('l','o','c','l')); | 
| 119 |   map->enable_feature (HB_TAG('c','c','m','p')); | 
| 120 |   map->enable_feature (HB_TAG('n','u','k','t')); | 
| 121 |   map->enable_feature (HB_TAG('a','k','h','n'), F_MANUAL_ZWJ); | 
| 122 |  | 
| 123 |   /* "Reordering group" */ | 
| 124 |   map->add_gsub_pause (_hb_clear_substitution_flags); | 
| 125 |   map->add_feature (HB_TAG('r','p','h','f'), F_MANUAL_ZWJ); | 
| 126 |   map->add_gsub_pause (record_rphf_use); | 
| 127 |   map->add_gsub_pause (_hb_clear_substitution_flags); | 
| 128 |   map->enable_feature (HB_TAG('p','r','e','f'), F_MANUAL_ZWJ); | 
| 129 |   map->add_gsub_pause (record_pref_use); | 
| 130 |  | 
| 131 |   /* "Orthographic unit shaping group" */ | 
| 132 |   for (unsigned int i = 0; i < ARRAY_LENGTH (use_basic_features); i++) | 
| 133 |     map->enable_feature (use_basic_features[i], F_MANUAL_ZWJ); | 
| 134 |  | 
| 135 |   map->add_gsub_pause (reorder_use); | 
| 136 |   map->add_gsub_pause (_hb_clear_syllables); | 
| 137 |  | 
| 138 |   /* "Topographical features" */ | 
| 139 |   for (unsigned int i = 0; i < ARRAY_LENGTH (use_topographical_features); i++) | 
| 140 |     map->add_feature (use_topographical_features[i]); | 
| 141 |   map->add_gsub_pause (nullptr); | 
| 142 |  | 
| 143 |   /* "Standard typographic presentation" */ | 
| 144 |   for (unsigned int i = 0; i < ARRAY_LENGTH (use_other_features); i++) | 
| 145 |     map->enable_feature (use_other_features[i], F_MANUAL_ZWJ); | 
| 146 | } | 
| 147 |  | 
| 148 | struct use_shape_plan_t | 
| 149 | { | 
| 150 |   hb_mask_t rphf_mask; | 
| 151 |  | 
| 152 |   arabic_shape_plan_t *arabic_plan; | 
| 153 | }; | 
| 154 |  | 
| 155 | static bool | 
| 156 | has_arabic_joining (hb_script_t script) | 
| 157 | { | 
| 158 |   /* List of scripts that have data in arabic-table. */ | 
| 159 |   switch ((int) script) | 
| 160 |   { | 
| 161 |     /* Unicode-1.1 additions */ | 
| 162 |     case HB_SCRIPT_ARABIC: | 
| 163 |  | 
| 164 |     /* Unicode-3.0 additions */ | 
| 165 |     case HB_SCRIPT_MONGOLIAN: | 
| 166 |     case HB_SCRIPT_SYRIAC: | 
| 167 |  | 
| 168 |     /* Unicode-5.0 additions */ | 
| 169 |     case HB_SCRIPT_NKO: | 
| 170 |     case HB_SCRIPT_PHAGS_PA: | 
| 171 |  | 
| 172 |     /* Unicode-6.0 additions */ | 
| 173 |     case HB_SCRIPT_MANDAIC: | 
| 174 |  | 
| 175 |     /* Unicode-7.0 additions */ | 
| 176 |     case HB_SCRIPT_MANICHAEAN: | 
| 177 |     case HB_SCRIPT_PSALTER_PAHLAVI: | 
| 178 |  | 
| 179 |     /* Unicode-9.0 additions */ | 
| 180 |     case HB_SCRIPT_ADLAM: | 
| 181 |  | 
| 182 |       return true; | 
| 183 |  | 
| 184 |     default: | 
| 185 |       return false; | 
| 186 |   } | 
| 187 | } | 
| 188 |  | 
| 189 | static void * | 
| 190 | data_create_use (const hb_ot_shape_plan_t *plan) | 
| 191 | { | 
| 192 |   use_shape_plan_t *use_plan = (use_shape_plan_t *) calloc (1, sizeof (use_shape_plan_t)); | 
| 193 |   if (unlikely (!use_plan)) | 
| 194 |     return nullptr; | 
| 195 |  | 
| 196 |   use_plan->rphf_mask = plan->map.get_1_mask (HB_TAG('r','p','h','f')); | 
| 197 |  | 
| 198 |   if (has_arabic_joining (plan->props.script)) | 
| 199 |   { | 
| 200 |     use_plan->arabic_plan = (arabic_shape_plan_t *) data_create_arabic (plan); | 
| 201 |     if (unlikely (!use_plan->arabic_plan)) | 
| 202 |     { | 
| 203 |       free (use_plan); | 
| 204 |       return nullptr; | 
| 205 |     } | 
| 206 |   } | 
| 207 |  | 
| 208 |   return use_plan; | 
| 209 | } | 
| 210 |  | 
| 211 | static void | 
| 212 | data_destroy_use (void *data) | 
| 213 | { | 
| 214 |   use_shape_plan_t *use_plan = (use_shape_plan_t *) data; | 
| 215 |  | 
| 216 |   if (use_plan->arabic_plan) | 
| 217 |     data_destroy_arabic (use_plan->arabic_plan); | 
| 218 |  | 
| 219 |   free (data); | 
| 220 | } | 
| 221 |  | 
| 222 | enum use_syllable_type_t { | 
| 223 |   use_independent_cluster, | 
| 224 |   use_virama_terminated_cluster, | 
| 225 |   use_sakot_terminated_cluster, | 
| 226 |   use_standard_cluster, | 
| 227 |   use_number_joiner_terminated_cluster, | 
| 228 |   use_numeral_cluster, | 
| 229 |   use_symbol_cluster, | 
| 230 |   use_broken_cluster, | 
| 231 |   use_non_cluster, | 
| 232 | }; | 
| 233 |  | 
| 234 | #include "hb-ot-shape-complex-use-machine.hh" | 
| 235 |  | 
| 236 |  | 
| 237 | static void | 
| 238 | setup_masks_use (const hb_ot_shape_plan_t *plan, | 
| 239 | 		 hb_buffer_t              *buffer, | 
| 240 | 		 hb_font_t                *font HB_UNUSED) | 
| 241 | { | 
| 242 |   const use_shape_plan_t *use_plan = (const use_shape_plan_t *) plan->data; | 
| 243 |  | 
| 244 |   /* Do this before allocating use_category(). */ | 
| 245 |   if (use_plan->arabic_plan) | 
| 246 |   { | 
| 247 |     setup_masks_arabic_plan (use_plan->arabic_plan, buffer, plan->props.script); | 
| 248 |   } | 
| 249 |  | 
| 250 |   HB_BUFFER_ALLOCATE_VAR (buffer, use_category); | 
| 251 |  | 
| 252 |   /* We cannot setup masks here.  We save information about characters | 
| 253 |    * and setup masks later on in a pause-callback. */ | 
| 254 |  | 
| 255 |   unsigned int count = buffer->len; | 
| 256 |   hb_glyph_info_t *info = buffer->info; | 
| 257 |   for (unsigned int i = 0; i < count; i++) | 
| 258 |     info[i].use_category() = hb_use_get_category (info[i].codepoint); | 
| 259 | } | 
| 260 |  | 
| 261 | static void | 
| 262 | setup_rphf_mask (const hb_ot_shape_plan_t *plan, | 
| 263 | 		 hb_buffer_t *buffer) | 
| 264 | { | 
| 265 |   const use_shape_plan_t *use_plan = (const use_shape_plan_t *) plan->data; | 
| 266 |  | 
| 267 |   hb_mask_t mask = use_plan->rphf_mask; | 
| 268 |   if (!mask) return; | 
| 269 |  | 
| 270 |   hb_glyph_info_t *info = buffer->info; | 
| 271 |  | 
| 272 |   foreach_syllable (buffer, start, end) | 
| 273 |   { | 
| 274 |     unsigned int limit = info[start].use_category() == USE_R ? 1 : hb_min (3u, end - start); | 
| 275 |     for (unsigned int i = start; i < start + limit; i++) | 
| 276 |       info[i].mask |= mask; | 
| 277 |   } | 
| 278 | } | 
| 279 |  | 
| 280 | static void | 
| 281 | setup_topographical_masks (const hb_ot_shape_plan_t *plan, | 
| 282 | 			   hb_buffer_t *buffer) | 
| 283 | { | 
| 284 |   const use_shape_plan_t *use_plan = (const use_shape_plan_t *) plan->data; | 
| 285 |   if (use_plan->arabic_plan) | 
| 286 |     return; | 
| 287 |  | 
| 288 |   static_assert ((USE_INIT < 4 && USE_ISOL < 4 && USE_MEDI < 4 && USE_FINA < 4), "" ); | 
| 289 |   hb_mask_t masks[4], all_masks = 0; | 
| 290 |   for (unsigned int i = 0; i < 4; i++) | 
| 291 |   { | 
| 292 |     masks[i] = plan->map.get_1_mask (use_topographical_features[i]); | 
| 293 |     if (masks[i] == plan->map.get_global_mask ()) | 
| 294 |       masks[i] = 0; | 
| 295 |     all_masks |= masks[i]; | 
| 296 |   } | 
| 297 |   if (!all_masks) | 
| 298 |     return; | 
| 299 |   hb_mask_t other_masks = ~all_masks; | 
| 300 |  | 
| 301 |   unsigned int last_start = 0; | 
| 302 |   joining_form_t last_form = _USE_NONE; | 
| 303 |   hb_glyph_info_t *info = buffer->info; | 
| 304 |   foreach_syllable (buffer, start, end) | 
| 305 |   { | 
| 306 |     use_syllable_type_t syllable_type = (use_syllable_type_t) (info[start].syllable() & 0x0F); | 
| 307 |     switch (syllable_type) | 
| 308 |     { | 
| 309 |       case use_independent_cluster: | 
| 310 |       case use_symbol_cluster: | 
| 311 |       case use_non_cluster: | 
| 312 | 	/* These don't join.  Nothing to do. */ | 
| 313 | 	last_form = _USE_NONE; | 
| 314 | 	break; | 
| 315 |  | 
| 316 |       case use_virama_terminated_cluster: | 
| 317 |       case use_sakot_terminated_cluster: | 
| 318 |       case use_standard_cluster: | 
| 319 |       case use_number_joiner_terminated_cluster: | 
| 320 |       case use_numeral_cluster: | 
| 321 |       case use_broken_cluster: | 
| 322 |  | 
| 323 | 	bool join = last_form == USE_FINA || last_form == USE_ISOL; | 
| 324 |  | 
| 325 | 	if (join) | 
| 326 | 	{ | 
| 327 | 	  /* Fixup previous syllable's form. */ | 
| 328 | 	  last_form = last_form == USE_FINA ? USE_MEDI : USE_INIT; | 
| 329 | 	  for (unsigned int i = last_start; i < start; i++) | 
| 330 | 	    info[i].mask = (info[i].mask & other_masks) | masks[last_form]; | 
| 331 | 	} | 
| 332 |  | 
| 333 | 	/* Form for this syllable. */ | 
| 334 | 	last_form = join ? USE_FINA : USE_ISOL; | 
| 335 | 	for (unsigned int i = start; i < end; i++) | 
| 336 | 	  info[i].mask = (info[i].mask & other_masks) | masks[last_form]; | 
| 337 |  | 
| 338 | 	break; | 
| 339 |     } | 
| 340 |  | 
| 341 |     last_start = start; | 
| 342 |   } | 
| 343 | } | 
| 344 |  | 
| 345 | static void | 
| 346 | setup_syllables_use (const hb_ot_shape_plan_t *plan, | 
| 347 | 		     hb_font_t *font HB_UNUSED, | 
| 348 | 		     hb_buffer_t *buffer) | 
| 349 | { | 
| 350 |   find_syllables_use (buffer); | 
| 351 |   foreach_syllable (buffer, start, end) | 
| 352 |     buffer->unsafe_to_break (start, end); | 
| 353 |   setup_rphf_mask (plan, buffer); | 
| 354 |   setup_topographical_masks (plan, buffer); | 
| 355 | } | 
| 356 |  | 
| 357 | static void | 
| 358 | record_rphf_use (const hb_ot_shape_plan_t *plan, | 
| 359 | 		 hb_font_t *font HB_UNUSED, | 
| 360 | 		 hb_buffer_t *buffer) | 
| 361 | { | 
| 362 |   const use_shape_plan_t *use_plan = (const use_shape_plan_t *) plan->data; | 
| 363 |  | 
| 364 |   hb_mask_t mask = use_plan->rphf_mask; | 
| 365 |   if (!mask) return; | 
| 366 |   hb_glyph_info_t *info = buffer->info; | 
| 367 |  | 
| 368 |   foreach_syllable (buffer, start, end) | 
| 369 |   { | 
| 370 |     /* Mark a substituted repha as USE_R. */ | 
| 371 |     for (unsigned int i = start; i < end && (info[i].mask & mask); i++) | 
| 372 |       if (_hb_glyph_info_substituted (&info[i])) | 
| 373 |       { | 
| 374 | 	info[i].use_category() = USE_R; | 
| 375 | 	break; | 
| 376 |       } | 
| 377 |   } | 
| 378 | } | 
| 379 |  | 
| 380 | static void | 
| 381 | record_pref_use (const hb_ot_shape_plan_t *plan HB_UNUSED, | 
| 382 | 		 hb_font_t *font HB_UNUSED, | 
| 383 | 		 hb_buffer_t *buffer) | 
| 384 | { | 
| 385 |   hb_glyph_info_t *info = buffer->info; | 
| 386 |  | 
| 387 |   foreach_syllable (buffer, start, end) | 
| 388 |   { | 
| 389 |     /* Mark a substituted pref as VPre, as they behave the same way. */ | 
| 390 |     for (unsigned int i = start; i < end; i++) | 
| 391 |       if (_hb_glyph_info_substituted (&info[i])) | 
| 392 |       { | 
| 393 | 	info[i].use_category() = USE_VPre; | 
| 394 | 	break; | 
| 395 |       } | 
| 396 |   } | 
| 397 | } | 
| 398 |  | 
| 399 | static inline bool | 
| 400 | is_halant_use (const hb_glyph_info_t &info) | 
| 401 | { | 
| 402 |   return (info.use_category() == USE_H || info.use_category() == USE_HVM) && | 
| 403 | 	 !_hb_glyph_info_ligated (&info); | 
| 404 | } | 
| 405 |  | 
| 406 | static void | 
| 407 | reorder_syllable_use (hb_buffer_t *buffer, unsigned int start, unsigned int end) | 
| 408 | { | 
| 409 |   use_syllable_type_t syllable_type = (use_syllable_type_t) (buffer->info[start].syllable() & 0x0F); | 
| 410 |   /* Only a few syllable types need reordering. */ | 
| 411 |   if (unlikely (!(FLAG_UNSAFE (syllable_type) & | 
| 412 | 		  (FLAG (use_virama_terminated_cluster) | | 
| 413 | 		   FLAG (use_sakot_terminated_cluster) | | 
| 414 | 		   FLAG (use_standard_cluster) | | 
| 415 | 		   FLAG (use_broken_cluster) | | 
| 416 | 		   0)))) | 
| 417 |     return; | 
| 418 |  | 
| 419 |   hb_glyph_info_t *info = buffer->info; | 
| 420 |  | 
| 421 | #define POST_BASE_FLAGS64 (FLAG64 (USE_FM) | \ | 
| 422 | 			   FLAG64 (USE_FAbv) | \ | 
| 423 | 			   FLAG64 (USE_FBlw) | \ | 
| 424 | 			   FLAG64 (USE_FPst) | \ | 
| 425 | 			   FLAG64 (USE_MAbv) | \ | 
| 426 | 			   FLAG64 (USE_MBlw) | \ | 
| 427 | 			   FLAG64 (USE_MPst) | \ | 
| 428 | 			   FLAG64 (USE_MPre) | \ | 
| 429 | 			   FLAG64 (USE_VAbv) | \ | 
| 430 | 			   FLAG64 (USE_VBlw) | \ | 
| 431 | 			   FLAG64 (USE_VPst) | \ | 
| 432 | 			   FLAG64 (USE_VPre) | \ | 
| 433 | 			   FLAG64 (USE_VMAbv) | \ | 
| 434 | 			   FLAG64 (USE_VMBlw) | \ | 
| 435 | 			   FLAG64 (USE_VMPst) | \ | 
| 436 | 			   FLAG64 (USE_VMPre)) | 
| 437 |  | 
| 438 |   /* Move things forward. */ | 
| 439 |   if (info[start].use_category() == USE_R && end - start > 1) | 
| 440 |   { | 
| 441 |     /* Got a repha.  Reorder it towards the end, but before the first post-base | 
| 442 |      * glyph. */ | 
| 443 |     for (unsigned int i = start + 1; i < end; i++) | 
| 444 |     { | 
| 445 |       bool is_post_base_glyph = (FLAG64_UNSAFE (info[i].use_category()) & POST_BASE_FLAGS64) || | 
| 446 | 				is_halant_use (info[i]); | 
| 447 |       if (is_post_base_glyph || i == end - 1) | 
| 448 |       { | 
| 449 | 	/* If we hit a post-base glyph, move before it; otherwise move to the | 
| 450 | 	 * end. Shift things in between backward. */ | 
| 451 |  | 
| 452 | 	if (is_post_base_glyph) | 
| 453 | 	  i--; | 
| 454 |  | 
| 455 | 	buffer->merge_clusters (start, i + 1); | 
| 456 | 	hb_glyph_info_t t = info[start]; | 
| 457 | 	memmove (&info[start], &info[start + 1], (i - start) * sizeof (info[0])); | 
| 458 | 	info[i] = t; | 
| 459 |  | 
| 460 | 	break; | 
| 461 |       } | 
| 462 |     } | 
| 463 |   } | 
| 464 |  | 
| 465 |   /* Move things back. */ | 
| 466 |   unsigned int j = start; | 
| 467 |   for (unsigned int i = start; i < end; i++) | 
| 468 |   { | 
| 469 |     uint32_t flag = FLAG_UNSAFE (info[i].use_category()); | 
| 470 |     if (is_halant_use (info[i])) | 
| 471 |     { | 
| 472 |       /* If we hit a halant, move after it; otherwise move to the beginning, and | 
| 473 |        * shift things in between forward. */ | 
| 474 |       j = i + 1; | 
| 475 |     } | 
| 476 |     else if (((flag) & (FLAG (USE_VPre) | FLAG (USE_VMPre))) && | 
| 477 | 	     /* Only move the first component of a MultipleSubst. */ | 
| 478 | 	     0 == _hb_glyph_info_get_lig_comp (&info[i]) && | 
| 479 | 	     j < i) | 
| 480 |     { | 
| 481 |       buffer->merge_clusters (j, i + 1); | 
| 482 |       hb_glyph_info_t t = info[i]; | 
| 483 |       memmove (&info[j + 1], &info[j], (i - j) * sizeof (info[0])); | 
| 484 |       info[j] = t; | 
| 485 |     } | 
| 486 |   } | 
| 487 | } | 
| 488 |  | 
| 489 | static inline void | 
| 490 | insert_dotted_circles_use (const hb_ot_shape_plan_t *plan HB_UNUSED, | 
| 491 | 			   hb_font_t *font, | 
| 492 | 			   hb_buffer_t *buffer) | 
| 493 | { | 
| 494 |   if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE)) | 
| 495 |     return; | 
| 496 |  | 
| 497 |   /* Note: This loop is extra overhead, but should not be measurable. | 
| 498 |    * TODO Use a buffer scratch flag to remove the loop. */ | 
| 499 |   bool has_broken_syllables = false; | 
| 500 |   unsigned int count = buffer->len; | 
| 501 |   hb_glyph_info_t *info = buffer->info; | 
| 502 |   for (unsigned int i = 0; i < count; i++) | 
| 503 |     if ((info[i].syllable() & 0x0F) == use_broken_cluster) | 
| 504 |     { | 
| 505 |       has_broken_syllables = true; | 
| 506 |       break; | 
| 507 |     } | 
| 508 |   if (likely (!has_broken_syllables)) | 
| 509 |     return; | 
| 510 |  | 
| 511 |   hb_glyph_info_t dottedcircle = {0}; | 
| 512 |   if (!font->get_nominal_glyph (0x25CCu, &dottedcircle.codepoint)) | 
| 513 |     return; | 
| 514 |   dottedcircle.use_category() = hb_use_get_category (0x25CC); | 
| 515 |  | 
| 516 |   buffer->clear_output (); | 
| 517 |  | 
| 518 |   buffer->idx = 0; | 
| 519 |   unsigned int last_syllable = 0; | 
| 520 |   while (buffer->idx < buffer->len && buffer->successful) | 
| 521 |   { | 
| 522 |     unsigned int syllable = buffer->cur().syllable(); | 
| 523 |     use_syllable_type_t syllable_type = (use_syllable_type_t) (syllable & 0x0F); | 
| 524 |     if (unlikely (last_syllable != syllable && syllable_type == use_broken_cluster)) | 
| 525 |     { | 
| 526 |       last_syllable = syllable; | 
| 527 |  | 
| 528 |       hb_glyph_info_t ginfo = dottedcircle; | 
| 529 |       ginfo.cluster = buffer->cur().cluster; | 
| 530 |       ginfo.mask = buffer->cur().mask; | 
| 531 |       ginfo.syllable() = buffer->cur().syllable(); | 
| 532 |  | 
| 533 |       /* Insert dottedcircle after possible Repha. */ | 
| 534 |       while (buffer->idx < buffer->len && buffer->successful && | 
| 535 | 	     last_syllable == buffer->cur().syllable() && | 
| 536 | 	     buffer->cur().use_category() == USE_R) | 
| 537 | 	buffer->next_glyph (); | 
| 538 |  | 
| 539 |       buffer->output_info (ginfo); | 
| 540 |     } | 
| 541 |     else | 
| 542 |       buffer->next_glyph (); | 
| 543 |   } | 
| 544 |   buffer->swap_buffers (); | 
| 545 | } | 
| 546 |  | 
| 547 | static void | 
| 548 | reorder_use (const hb_ot_shape_plan_t *plan, | 
| 549 | 	     hb_font_t *font, | 
| 550 | 	     hb_buffer_t *buffer) | 
| 551 | { | 
| 552 |   insert_dotted_circles_use (plan, font, buffer); | 
| 553 |  | 
| 554 |   foreach_syllable (buffer, start, end) | 
| 555 |     reorder_syllable_use (buffer, start, end); | 
| 556 |  | 
| 557 |   HB_BUFFER_DEALLOCATE_VAR (buffer, use_category); | 
| 558 | } | 
| 559 |  | 
| 560 |  | 
| 561 | static void | 
| 562 | preprocess_text_use (const hb_ot_shape_plan_t *plan, | 
| 563 | 		     hb_buffer_t              *buffer, | 
| 564 | 		     hb_font_t                *font) | 
| 565 | { | 
| 566 |   _hb_preprocess_text_vowel_constraints (plan, buffer, font); | 
| 567 | } | 
| 568 |  | 
| 569 | static bool | 
| 570 | compose_use (const hb_ot_shape_normalize_context_t *c, | 
| 571 | 	     hb_codepoint_t  a, | 
| 572 | 	     hb_codepoint_t  b, | 
| 573 | 	     hb_codepoint_t *ab) | 
| 574 | { | 
| 575 |   /* Avoid recomposing split matras. */ | 
| 576 |   if (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (c->unicode->general_category (a))) | 
| 577 |     return false; | 
| 578 |  | 
| 579 |   return (bool)c->unicode->compose (a, b, ab); | 
| 580 | } | 
| 581 |  | 
| 582 |  | 
| 583 | const hb_ot_complex_shaper_t _hb_ot_complex_shaper_use = | 
| 584 | { | 
| 585 |   collect_features_use, | 
| 586 |   nullptr, /* override_features */ | 
| 587 |   data_create_use, | 
| 588 |   data_destroy_use, | 
| 589 |   preprocess_text_use, | 
| 590 |   nullptr, /* postprocess_glyphs */ | 
| 591 |   HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT, | 
| 592 |   nullptr, /* decompose */ | 
| 593 |   compose_use, | 
| 594 |   setup_masks_use, | 
| 595 |   HB_TAG_NONE, /* gpos_tag */ | 
| 596 |   nullptr, /* reorder_marks */ | 
| 597 |   HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY, | 
| 598 |   false, /* fallback_position */ | 
| 599 | }; | 
| 600 |  | 
| 601 |  | 
| 602 | #endif | 
| 603 |  |