Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/harfbuzz/src/hb-ot-shaper-use.cc @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 1:1d09e1dec1d9 | 2:b50eed0cc0ef |
|---|---|
| 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-shaper-use-machine.hh" | |
| 34 #include "hb-ot-shaper-use-table.hh" | |
| 35 #include "hb-ot-shaper-arabic.hh" | |
| 36 #include "hb-ot-shaper-arabic-joining-list.hh" | |
| 37 #include "hb-ot-shaper-vowel-constraints.hh" | |
| 38 | |
| 39 | |
| 40 /* | |
| 41 * Universal Shaping Engine. | |
| 42 * https://docs.microsoft.com/en-us/typography/script-development/use | |
| 43 */ | |
| 44 | |
| 45 static const hb_tag_t | |
| 46 use_basic_features[] = | |
| 47 { | |
| 48 /* | |
| 49 * Basic features. | |
| 50 * These features are applied all at once, before reordering, constrained | |
| 51 * to the syllable. | |
| 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 JOINING_FORM_ISOL, | |
| 72 JOINING_FORM_INIT, | |
| 73 JOINING_FORM_MEDI, | |
| 74 JOINING_FORM_FINA, | |
| 75 _JOINING_FORM_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 bool | |
| 93 setup_syllables_use (const hb_ot_shape_plan_t *plan, | |
| 94 hb_font_t *font, | |
| 95 hb_buffer_t *buffer); | |
| 96 static bool | |
| 97 record_rphf_use (const hb_ot_shape_plan_t *plan, | |
| 98 hb_font_t *font, | |
| 99 hb_buffer_t *buffer); | |
| 100 static bool | |
| 101 record_pref_use (const hb_ot_shape_plan_t *plan, | |
| 102 hb_font_t *font, | |
| 103 hb_buffer_t *buffer); | |
| 104 static bool | |
| 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'), F_PER_SYLLABLE); | |
| 119 map->enable_feature (HB_TAG('c','c','m','p'), F_PER_SYLLABLE); | |
| 120 map->enable_feature (HB_TAG('n','u','k','t'), F_PER_SYLLABLE); | |
| 121 map->enable_feature (HB_TAG('a','k','h','n'), F_MANUAL_ZWJ | F_PER_SYLLABLE); | |
| 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 | F_PER_SYLLABLE); | |
| 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 | F_PER_SYLLABLE); | |
| 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 | F_PER_SYLLABLE); | |
| 134 | |
| 135 map->add_gsub_pause (reorder_use); | |
| 136 map->add_gsub_pause (hb_syllabic_clear_var); // Don't need syllables anymore, use stop to free buffer var | |
| 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 void * | |
| 156 data_create_use (const hb_ot_shape_plan_t *plan) | |
| 157 { | |
| 158 use_shape_plan_t *use_plan = (use_shape_plan_t *) hb_calloc (1, sizeof (use_shape_plan_t)); | |
| 159 if (unlikely (!use_plan)) | |
| 160 return nullptr; | |
| 161 | |
| 162 use_plan->rphf_mask = plan->map.get_1_mask (HB_TAG('r','p','h','f')); | |
| 163 | |
| 164 if (has_arabic_joining (plan->props.script)) | |
| 165 { | |
| 166 use_plan->arabic_plan = (arabic_shape_plan_t *) data_create_arabic (plan); | |
| 167 if (unlikely (!use_plan->arabic_plan)) | |
| 168 { | |
| 169 hb_free (use_plan); | |
| 170 return nullptr; | |
| 171 } | |
| 172 } | |
| 173 | |
| 174 return use_plan; | |
| 175 } | |
| 176 | |
| 177 static void | |
| 178 data_destroy_use (void *data) | |
| 179 { | |
| 180 use_shape_plan_t *use_plan = (use_shape_plan_t *) data; | |
| 181 | |
| 182 if (use_plan->arabic_plan) | |
| 183 data_destroy_arabic (use_plan->arabic_plan); | |
| 184 | |
| 185 hb_free (data); | |
| 186 } | |
| 187 | |
| 188 static void | |
| 189 setup_masks_use (const hb_ot_shape_plan_t *plan, | |
| 190 hb_buffer_t *buffer, | |
| 191 hb_font_t *font HB_UNUSED) | |
| 192 { | |
| 193 const use_shape_plan_t *use_plan = (const use_shape_plan_t *) plan->data; | |
| 194 | |
| 195 /* Do this before allocating use_category(). */ | |
| 196 if (use_plan->arabic_plan) | |
| 197 { | |
| 198 setup_masks_arabic_plan (use_plan->arabic_plan, buffer, plan->props.script); | |
| 199 } | |
| 200 | |
| 201 HB_BUFFER_ALLOCATE_VAR (buffer, use_category); | |
| 202 | |
| 203 /* We cannot setup masks here. We save information about characters | |
| 204 * and setup masks later on in a pause-callback. */ | |
| 205 | |
| 206 unsigned int count = buffer->len; | |
| 207 hb_glyph_info_t *info = buffer->info; | |
| 208 for (unsigned int i = 0; i < count; i++) | |
| 209 info[i].use_category() = hb_use_get_category (info[i].codepoint); | |
| 210 } | |
| 211 | |
| 212 static void | |
| 213 setup_rphf_mask (const hb_ot_shape_plan_t *plan, | |
| 214 hb_buffer_t *buffer) | |
| 215 { | |
| 216 const use_shape_plan_t *use_plan = (const use_shape_plan_t *) plan->data; | |
| 217 | |
| 218 hb_mask_t mask = use_plan->rphf_mask; | |
| 219 if (!mask) return; | |
| 220 | |
| 221 hb_glyph_info_t *info = buffer->info; | |
| 222 | |
| 223 foreach_syllable (buffer, start, end) | |
| 224 { | |
| 225 unsigned int limit = info[start].use_category() == USE(R) ? 1 : hb_min (3u, end - start); | |
| 226 for (unsigned int i = start; i < start + limit; i++) | |
| 227 info[i].mask |= mask; | |
| 228 } | |
| 229 } | |
| 230 | |
| 231 static void | |
| 232 setup_topographical_masks (const hb_ot_shape_plan_t *plan, | |
| 233 hb_buffer_t *buffer) | |
| 234 { | |
| 235 const use_shape_plan_t *use_plan = (const use_shape_plan_t *) plan->data; | |
| 236 if (use_plan->arabic_plan) | |
| 237 return; | |
| 238 | |
| 239 static_assert ((JOINING_FORM_INIT < 4 && JOINING_FORM_ISOL < 4 && JOINING_FORM_MEDI < 4 && JOINING_FORM_FINA < 4), ""); | |
| 240 hb_mask_t masks[4], all_masks = 0; | |
| 241 for (unsigned int i = 0; i < 4; i++) | |
| 242 { | |
| 243 masks[i] = plan->map.get_1_mask (use_topographical_features[i]); | |
| 244 if (masks[i] == plan->map.get_global_mask ()) | |
| 245 masks[i] = 0; | |
| 246 all_masks |= masks[i]; | |
| 247 } | |
| 248 if (!all_masks) | |
| 249 return; | |
| 250 hb_mask_t other_masks = ~all_masks; | |
| 251 | |
| 252 unsigned int last_start = 0; | |
| 253 joining_form_t last_form = _JOINING_FORM_NONE; | |
| 254 hb_glyph_info_t *info = buffer->info; | |
| 255 foreach_syllable (buffer, start, end) | |
| 256 { | |
| 257 use_syllable_type_t syllable_type = (use_syllable_type_t) (info[start].syllable() & 0x0F); | |
| 258 switch (syllable_type) | |
| 259 { | |
| 260 case use_hieroglyph_cluster: | |
| 261 case use_non_cluster: | |
| 262 /* These don't join. Nothing to do. */ | |
| 263 last_form = _JOINING_FORM_NONE; | |
| 264 break; | |
| 265 | |
| 266 case use_virama_terminated_cluster: | |
| 267 case use_sakot_terminated_cluster: | |
| 268 case use_standard_cluster: | |
| 269 case use_number_joiner_terminated_cluster: | |
| 270 case use_numeral_cluster: | |
| 271 case use_symbol_cluster: | |
| 272 case use_broken_cluster: | |
| 273 | |
| 274 bool join = last_form == JOINING_FORM_FINA || last_form == JOINING_FORM_ISOL; | |
| 275 | |
| 276 if (join) | |
| 277 { | |
| 278 /* Fixup previous syllable's form. */ | |
| 279 last_form = last_form == JOINING_FORM_FINA ? JOINING_FORM_MEDI : JOINING_FORM_INIT; | |
| 280 for (unsigned int i = last_start; i < start; i++) | |
| 281 info[i].mask = (info[i].mask & other_masks) | masks[last_form]; | |
| 282 } | |
| 283 | |
| 284 /* Form for this syllable. */ | |
| 285 last_form = join ? JOINING_FORM_FINA : JOINING_FORM_ISOL; | |
| 286 for (unsigned int i = start; i < end; i++) | |
| 287 info[i].mask = (info[i].mask & other_masks) | masks[last_form]; | |
| 288 | |
| 289 break; | |
| 290 } | |
| 291 | |
| 292 last_start = start; | |
| 293 } | |
| 294 } | |
| 295 | |
| 296 static bool | |
| 297 setup_syllables_use (const hb_ot_shape_plan_t *plan, | |
| 298 hb_font_t *font HB_UNUSED, | |
| 299 hb_buffer_t *buffer) | |
| 300 { | |
| 301 HB_BUFFER_ALLOCATE_VAR (buffer, syllable); | |
| 302 find_syllables_use (buffer); | |
| 303 foreach_syllable (buffer, start, end) | |
| 304 buffer->unsafe_to_break (start, end); | |
| 305 setup_rphf_mask (plan, buffer); | |
| 306 setup_topographical_masks (plan, buffer); | |
| 307 return false; | |
| 308 } | |
| 309 | |
| 310 static bool | |
| 311 record_rphf_use (const hb_ot_shape_plan_t *plan, | |
| 312 hb_font_t *font HB_UNUSED, | |
| 313 hb_buffer_t *buffer) | |
| 314 { | |
| 315 const use_shape_plan_t *use_plan = (const use_shape_plan_t *) plan->data; | |
| 316 | |
| 317 hb_mask_t mask = use_plan->rphf_mask; | |
| 318 if (!mask) return false; | |
| 319 hb_glyph_info_t *info = buffer->info; | |
| 320 | |
| 321 foreach_syllable (buffer, start, end) | |
| 322 { | |
| 323 /* Mark a substituted repha as USE(R). */ | |
| 324 for (unsigned int i = start; i < end && (info[i].mask & mask); i++) | |
| 325 if (_hb_glyph_info_substituted (&info[i])) | |
| 326 { | |
| 327 info[i].use_category() = USE(R); | |
| 328 break; | |
| 329 } | |
| 330 } | |
| 331 return false; | |
| 332 } | |
| 333 | |
| 334 static bool | |
| 335 record_pref_use (const hb_ot_shape_plan_t *plan HB_UNUSED, | |
| 336 hb_font_t *font HB_UNUSED, | |
| 337 hb_buffer_t *buffer) | |
| 338 { | |
| 339 hb_glyph_info_t *info = buffer->info; | |
| 340 | |
| 341 foreach_syllable (buffer, start, end) | |
| 342 { | |
| 343 /* Mark a substituted pref as VPre, as they behave the same way. */ | |
| 344 for (unsigned int i = start; i < end; i++) | |
| 345 if (_hb_glyph_info_substituted (&info[i])) | |
| 346 { | |
| 347 info[i].use_category() = USE(VPre); | |
| 348 break; | |
| 349 } | |
| 350 } | |
| 351 return false; | |
| 352 } | |
| 353 | |
| 354 static inline bool | |
| 355 is_halant_use (const hb_glyph_info_t &info) | |
| 356 { | |
| 357 return (info.use_category() == USE(H) || info.use_category() == USE(HVM) || info.use_category() == USE(IS)) && | |
| 358 !_hb_glyph_info_ligated (&info); | |
| 359 } | |
| 360 | |
| 361 static void | |
| 362 reorder_syllable_use (hb_buffer_t *buffer, unsigned int start, unsigned int end) | |
| 363 { | |
| 364 use_syllable_type_t syllable_type = (use_syllable_type_t) (buffer->info[start].syllable() & 0x0F); | |
| 365 /* Only a few syllable types need reordering. */ | |
| 366 if (unlikely (!(FLAG_UNSAFE (syllable_type) & | |
| 367 (FLAG (use_virama_terminated_cluster) | | |
| 368 FLAG (use_sakot_terminated_cluster) | | |
| 369 FLAG (use_standard_cluster) | | |
| 370 FLAG (use_symbol_cluster) | | |
| 371 FLAG (use_broken_cluster) | | |
| 372 0)))) | |
| 373 return; | |
| 374 | |
| 375 hb_glyph_info_t *info = buffer->info; | |
| 376 | |
| 377 #define POST_BASE_FLAGS64 (FLAG64 (USE(FAbv)) | \ | |
| 378 FLAG64 (USE(FBlw)) | \ | |
| 379 FLAG64 (USE(FPst)) | \ | |
| 380 FLAG64 (USE(MAbv)) | \ | |
| 381 FLAG64 (USE(MBlw)) | \ | |
| 382 FLAG64 (USE(MPst)) | \ | |
| 383 FLAG64 (USE(MPre)) | \ | |
| 384 FLAG64 (USE(VAbv)) | \ | |
| 385 FLAG64 (USE(VBlw)) | \ | |
| 386 FLAG64 (USE(VPst)) | \ | |
| 387 FLAG64 (USE(VPre)) | \ | |
| 388 FLAG64 (USE(VMAbv)) | \ | |
| 389 FLAG64 (USE(VMBlw)) | \ | |
| 390 FLAG64 (USE(VMPst)) | \ | |
| 391 FLAG64 (USE(VMPre))) | |
| 392 | |
| 393 /* Move things forward. */ | |
| 394 if (info[start].use_category() == USE(R) && end - start > 1) | |
| 395 { | |
| 396 /* Got a repha. Reorder it towards the end, but before the first post-base | |
| 397 * glyph. */ | |
| 398 for (unsigned int i = start + 1; i < end; i++) | |
| 399 { | |
| 400 bool is_post_base_glyph = (FLAG64_UNSAFE (info[i].use_category()) & POST_BASE_FLAGS64) || | |
| 401 is_halant_use (info[i]); | |
| 402 if (is_post_base_glyph || i == end - 1) | |
| 403 { | |
| 404 /* If we hit a post-base glyph, move before it; otherwise move to the | |
| 405 * end. Shift things in between backward. */ | |
| 406 | |
| 407 if (is_post_base_glyph) | |
| 408 i--; | |
| 409 | |
| 410 buffer->merge_clusters (start, i + 1); | |
| 411 hb_glyph_info_t t = info[start]; | |
| 412 memmove (&info[start], &info[start + 1], (i - start) * sizeof (info[0])); | |
| 413 info[i] = t; | |
| 414 | |
| 415 break; | |
| 416 } | |
| 417 } | |
| 418 } | |
| 419 | |
| 420 /* Move things back. */ | |
| 421 unsigned int j = start; | |
| 422 for (unsigned int i = start; i < end; i++) | |
| 423 { | |
| 424 uint32_t flag = FLAG_UNSAFE (info[i].use_category()); | |
| 425 if (is_halant_use (info[i])) | |
| 426 { | |
| 427 /* If we hit a halant, move after it; otherwise move to the beginning, and | |
| 428 * shift things in between forward. */ | |
| 429 j = i + 1; | |
| 430 } | |
| 431 else if (((flag) & (FLAG (USE(VPre)) | FLAG (USE(VMPre)))) && | |
| 432 /* Only move the first component of a MultipleSubst. */ | |
| 433 0 == _hb_glyph_info_get_lig_comp (&info[i]) && | |
| 434 j < i) | |
| 435 { | |
| 436 buffer->merge_clusters (j, i + 1); | |
| 437 hb_glyph_info_t t = info[i]; | |
| 438 memmove (&info[j + 1], &info[j], (i - j) * sizeof (info[0])); | |
| 439 info[j] = t; | |
| 440 } | |
| 441 } | |
| 442 } | |
| 443 | |
| 444 static bool | |
| 445 reorder_use (const hb_ot_shape_plan_t *plan, | |
| 446 hb_font_t *font, | |
| 447 hb_buffer_t *buffer) | |
| 448 { | |
| 449 bool ret = false; | |
| 450 if (buffer->message (font, "start reordering USE")) | |
| 451 { | |
| 452 if (hb_syllabic_insert_dotted_circles (font, buffer, | |
| 453 use_broken_cluster, | |
| 454 USE(B), | |
| 455 USE(R))) | |
| 456 ret = true; | |
| 457 | |
| 458 foreach_syllable (buffer, start, end) | |
| 459 reorder_syllable_use (buffer, start, end); | |
| 460 | |
| 461 (void) buffer->message (font, "end reordering USE"); | |
| 462 } | |
| 463 | |
| 464 HB_BUFFER_DEALLOCATE_VAR (buffer, use_category); | |
| 465 | |
| 466 return ret; | |
| 467 } | |
| 468 | |
| 469 | |
| 470 static void | |
| 471 preprocess_text_use (const hb_ot_shape_plan_t *plan, | |
| 472 hb_buffer_t *buffer, | |
| 473 hb_font_t *font) | |
| 474 { | |
| 475 _hb_preprocess_text_vowel_constraints (plan, buffer, font); | |
| 476 } | |
| 477 | |
| 478 static bool | |
| 479 compose_use (const hb_ot_shape_normalize_context_t *c, | |
| 480 hb_codepoint_t a, | |
| 481 hb_codepoint_t b, | |
| 482 hb_codepoint_t *ab) | |
| 483 { | |
| 484 /* Avoid recomposing split matras. */ | |
| 485 if (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (c->unicode->general_category (a))) | |
| 486 return false; | |
| 487 | |
| 488 return (bool)c->unicode->compose (a, b, ab); | |
| 489 } | |
| 490 | |
| 491 | |
| 492 const hb_ot_shaper_t _hb_ot_shaper_use = | |
| 493 { | |
| 494 collect_features_use, | |
| 495 nullptr, /* override_features */ | |
| 496 data_create_use, | |
| 497 data_destroy_use, | |
| 498 preprocess_text_use, | |
| 499 nullptr, /* postprocess_glyphs */ | |
| 500 nullptr, /* decompose */ | |
| 501 compose_use, | |
| 502 setup_masks_use, | |
| 503 nullptr, /* reorder_marks */ | |
| 504 HB_TAG_NONE, /* gpos_tag */ | |
| 505 HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT, | |
| 506 HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY, | |
| 507 false, /* fallback_position */ | |
| 508 }; | |
| 509 | |
| 510 | |
| 511 #endif |
