hb-ot-shape.cc (39505B)
1 /* 2 * Copyright © 2009,2010 Red Hat, Inc. 3 * Copyright © 2010,2011,2012 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 * Red Hat Author(s): Behdad Esfahbod 26 * Google Author(s): Behdad Esfahbod 27 */ 28 29 #include "hb.hh" 30 31 #ifndef HB_NO_OT_SHAPE 32 33 #ifdef HB_NO_OT_LAYOUT 34 #error "Cannot compile 'ot' shaper with HB_NO_OT_LAYOUT." 35 #endif 36 37 #include "hb-shaper-impl.hh" 38 39 #include "hb-ot-shape.hh" 40 #include "hb-ot-shaper.hh" 41 #include "hb-ot-shape-fallback.hh" 42 #include "hb-ot-shape-normalize.hh" 43 44 #include "hb-ot-face.hh" 45 46 #include "hb-set.hh" 47 #include "hb-unicode.hh" 48 49 #include "hb-aat-layout.hh" 50 #include "hb-ot-layout-gdef-table.hh" 51 #include "hb-ot-stat-table.hh" 52 53 54 static inline bool 55 _hb_codepoint_is_regional_indicator (hb_codepoint_t u) 56 { return hb_in_range<hb_codepoint_t> (u, 0x1F1E6u, 0x1F1FFu); } 57 58 #ifndef HB_NO_AAT_SHAPE 59 static inline bool 60 _hb_apply_morx (hb_face_t *face, const hb_segment_properties_t &props) 61 { 62 /* https://github.com/harfbuzz/harfbuzz/issues/2124 */ 63 return hb_aat_layout_has_substitution (face) && 64 (HB_DIRECTION_IS_HORIZONTAL (props.direction) || !hb_ot_layout_has_substitution (face)); 65 } 66 #endif 67 68 /** 69 * SECTION:hb-ot-shape 70 * @title: hb-ot-shape 71 * @short_description: OpenType shaping support 72 * @include: hb-ot.h 73 * 74 * Support functions for OpenType shaping related queries. 75 **/ 76 77 78 static void 79 hb_ot_shape_collect_features (hb_ot_shape_planner_t *planner, 80 const hb_feature_t *user_features, 81 unsigned int num_user_features); 82 83 hb_ot_shape_planner_t::hb_ot_shape_planner_t (hb_face_t *face, 84 const hb_segment_properties_t &props) : 85 face (face), 86 props (props), 87 map (face, props) 88 #ifndef HB_NO_AAT_SHAPE 89 , aat_map (face, props) 90 , apply_morx (_hb_apply_morx (face, props)) 91 #endif 92 { 93 shaper = hb_ot_shaper_categorize (props.script, props.direction, map.chosen_script[0]); 94 95 script_zero_marks = shaper->zero_width_marks != HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE; 96 script_fallback_position = shaper->fallback_position; 97 98 #ifndef HB_NO_AAT_SHAPE 99 /* https://github.com/harfbuzz/harfbuzz/issues/1528 */ 100 if (apply_morx && shaper != &_hb_ot_shaper_default) 101 shaper = &_hb_ot_shaper_dumber; 102 #endif 103 } 104 105 void 106 hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t &plan, 107 const hb_ot_shape_plan_key_t &key) 108 { 109 plan.props = props; 110 plan.shaper = shaper; 111 map.compile (plan.map, key); 112 #ifndef HB_NO_AAT_SHAPE 113 if (apply_morx) 114 aat_map.compile (plan.aat_map); 115 #endif 116 117 #ifndef HB_NO_OT_SHAPE_FRACTIONS 118 plan.frac_mask = plan.map.get_1_mask (HB_TAG ('f','r','a','c')); 119 plan.numr_mask = plan.map.get_1_mask (HB_TAG ('n','u','m','r')); 120 plan.dnom_mask = plan.map.get_1_mask (HB_TAG ('d','n','o','m')); 121 plan.has_frac = plan.frac_mask || (plan.numr_mask && plan.dnom_mask); 122 #endif 123 124 plan.rtlm_mask = plan.map.get_1_mask (HB_TAG ('r','t','l','m')); 125 plan.has_vert = !!plan.map.get_1_mask (HB_TAG ('v','e','r','t')); 126 127 hb_tag_t kern_tag = HB_DIRECTION_IS_HORIZONTAL (props.direction) ? 128 HB_TAG ('k','e','r','n') : HB_TAG ('v','k','r','n'); 129 #ifndef HB_NO_OT_KERN 130 plan.kern_mask = plan.map.get_mask (kern_tag); 131 plan.requested_kerning = !!plan.kern_mask; 132 #endif 133 134 bool has_gpos_kern = plan.map.get_feature_index (1, kern_tag) != HB_OT_LAYOUT_NO_FEATURE_INDEX; 135 bool disable_gpos = plan.shaper->gpos_tag && 136 plan.shaper->gpos_tag != plan.map.chosen_script[1]; 137 138 /* 139 * Decide who provides glyph classes. GDEF or Unicode. 140 */ 141 142 if (!hb_ot_layout_has_glyph_classes (face)) 143 plan.fallback_glyph_classes = true; 144 145 /* 146 * Decide who does substitutions. GSUB, morx, or fallback. 147 */ 148 149 #ifndef HB_NO_AAT_SHAPE 150 plan.apply_morx = apply_morx; 151 #endif 152 153 /* 154 * Decide who does positioning. GPOS, kerx, kern, or fallback. 155 */ 156 157 #ifndef HB_NO_AAT_SHAPE 158 bool has_kerx = hb_aat_layout_has_positioning (face); 159 bool has_gsub = !apply_morx && hb_ot_layout_has_substitution (face); 160 #endif 161 bool has_gpos = !disable_gpos && hb_ot_layout_has_positioning (face); 162 if (false) 163 {} 164 #ifndef HB_NO_AAT_SHAPE 165 /* Prefer GPOS over kerx if GSUB is present; 166 * https://github.com/harfbuzz/harfbuzz/issues/3008 */ 167 else if (has_kerx && !(has_gsub && has_gpos)) 168 plan.apply_kerx = true; 169 #endif 170 else if (has_gpos) 171 plan.apply_gpos = true; 172 173 if (!plan.apply_kerx && (!has_gpos_kern || !plan.apply_gpos)) 174 { 175 if (false) {} 176 #ifndef HB_NO_AAT_SHAPE 177 else if (has_kerx) 178 plan.apply_kerx = true; 179 #endif 180 #ifndef HB_NO_OT_KERN 181 else if (hb_ot_layout_has_kerning (face)) 182 plan.apply_kern = script_fallback_position; // Not all shapers apply legacy `kern` 183 #endif 184 else {} 185 } 186 187 plan.apply_fallback_kern = script_fallback_position && !(plan.apply_gpos || plan.apply_kerx || plan.apply_kern); 188 189 plan.zero_marks = script_zero_marks && 190 !plan.apply_kerx && 191 (!plan.apply_kern 192 #ifndef HB_NO_OT_KERN 193 || !hb_ot_layout_has_machine_kerning (face) 194 #endif 195 ); 196 plan.has_gpos_mark = !!plan.map.get_1_mask (HB_TAG ('m','a','r','k')); 197 198 plan.adjust_mark_positioning_when_zeroing = !plan.apply_gpos && 199 !plan.apply_kerx && 200 (!plan.apply_kern 201 #ifndef HB_NO_OT_KERN 202 || !hb_ot_layout_has_cross_kerning (face) 203 #endif 204 ); 205 206 plan.fallback_mark_positioning = plan.adjust_mark_positioning_when_zeroing && 207 script_fallback_position; 208 209 #ifndef HB_NO_AAT_SHAPE 210 /* If we're using morx shaping, we cancel mark position adjustment because 211 Apple Color Emoji assumes this will NOT be done when forming emoji sequences; 212 https://github.com/harfbuzz/harfbuzz/issues/2967. */ 213 if (plan.apply_morx) 214 plan.adjust_mark_positioning_when_zeroing = false; 215 216 /* According to Ned, trak is applied by default for "modern fonts", as detected by presence of STAT table. */ 217 #ifndef HB_NO_STYLE 218 plan.apply_trak = hb_aat_layout_has_tracking (face) && face->table.STAT->has_data (); 219 #else 220 plan.apply_trak = false; 221 #endif 222 223 #endif 224 } 225 226 bool 227 hb_ot_shape_plan_t::init0 (hb_face_t *face, 228 const hb_shape_plan_key_t *key) 229 { 230 map.init (); 231 232 hb_ot_shape_planner_t planner (face, 233 key->props); 234 235 hb_ot_shape_collect_features (&planner, 236 key->user_features, 237 key->num_user_features); 238 239 planner.compile (*this, key->ot); 240 241 if (shaper->data_create) 242 { 243 data = shaper->data_create (this); 244 if (unlikely (!data)) 245 { 246 map.fini (); 247 return false; 248 } 249 } 250 251 return true; 252 } 253 254 void 255 hb_ot_shape_plan_t::fini () 256 { 257 if (shaper->data_destroy) 258 shaper->data_destroy (const_cast<void *> (data)); 259 260 map.fini (); 261 } 262 263 void 264 hb_ot_shape_plan_t::substitute (hb_font_t *font, 265 hb_buffer_t *buffer) const 266 { 267 map.substitute (this, font, buffer); 268 } 269 270 void 271 hb_ot_shape_plan_t::position (hb_font_t *font, 272 hb_buffer_t *buffer) const 273 { 274 if (this->apply_gpos) 275 map.position (this, font, buffer); 276 #ifndef HB_NO_AAT_SHAPE 277 else if (this->apply_kerx) 278 hb_aat_layout_position (this, font, buffer); 279 #endif 280 281 #ifndef HB_NO_OT_KERN 282 if (this->apply_kern) 283 hb_ot_layout_kern (this, font, buffer); 284 #endif 285 else if (this->apply_fallback_kern) 286 _hb_ot_shape_fallback_kern (this, font, buffer); 287 288 #ifndef HB_NO_AAT_SHAPE 289 if (this->apply_trak) 290 hb_aat_layout_track (this, font, buffer); 291 #endif 292 } 293 294 295 static const hb_ot_map_feature_t 296 common_features[] = 297 { 298 {HB_TAG('a','b','v','m'), F_GLOBAL}, 299 {HB_TAG('b','l','w','m'), F_GLOBAL}, 300 {HB_TAG('c','c','m','p'), F_GLOBAL}, 301 {HB_TAG('l','o','c','l'), F_GLOBAL}, 302 {HB_TAG('m','a','r','k'), F_GLOBAL_MANUAL_JOINERS}, 303 {HB_TAG('m','k','m','k'), F_GLOBAL_MANUAL_JOINERS}, 304 {HB_TAG('r','l','i','g'), F_GLOBAL}, 305 }; 306 307 308 static const hb_ot_map_feature_t 309 horizontal_features[] = 310 { 311 {HB_TAG('c','a','l','t'), F_GLOBAL}, 312 {HB_TAG('c','l','i','g'), F_GLOBAL}, 313 {HB_TAG('c','u','r','s'), F_GLOBAL}, 314 {HB_TAG('d','i','s','t'), F_GLOBAL}, 315 {HB_TAG('k','e','r','n'), F_GLOBAL_HAS_FALLBACK}, 316 {HB_TAG('l','i','g','a'), F_GLOBAL}, 317 {HB_TAG('r','c','l','t'), F_GLOBAL}, 318 }; 319 320 static void 321 hb_ot_shape_collect_features (hb_ot_shape_planner_t *planner, 322 const hb_feature_t *user_features, 323 unsigned int num_user_features) 324 { 325 hb_ot_map_builder_t *map = &planner->map; 326 327 map->is_simple = true; 328 329 map->enable_feature (HB_TAG('r','v','r','n')); 330 map->add_gsub_pause (nullptr); 331 332 switch (planner->props.direction) 333 { 334 case HB_DIRECTION_LTR: 335 map->enable_feature (HB_TAG ('l','t','r','a')); 336 map->enable_feature (HB_TAG ('l','t','r','m')); 337 break; 338 case HB_DIRECTION_RTL: 339 map->enable_feature (HB_TAG ('r','t','l','a')); 340 map->add_feature (HB_TAG ('r','t','l','m')); 341 break; 342 case HB_DIRECTION_TTB: 343 case HB_DIRECTION_BTT: 344 case HB_DIRECTION_INVALID: 345 default: 346 break; 347 } 348 349 #ifndef HB_NO_OT_SHAPE_FRACTIONS 350 /* Automatic fractions. */ 351 map->add_feature (HB_TAG ('f','r','a','c')); 352 map->add_feature (HB_TAG ('n','u','m','r')); 353 map->add_feature (HB_TAG ('d','n','o','m')); 354 #endif 355 356 /* Random! */ 357 map->enable_feature (HB_TAG ('r','a','n','d'), F_RANDOM, HB_OT_MAP_MAX_VALUE); 358 359 map->enable_feature (HB_TAG ('H','a','r','f')); /* Considered required. */ 360 map->enable_feature (HB_TAG ('H','A','R','F')); /* Considered discretionary. */ 361 362 if (planner->shaper->collect_features) 363 { 364 map->is_simple = false; 365 planner->shaper->collect_features (planner); 366 } 367 368 map->enable_feature (HB_TAG ('B','u','z','z')); /* Considered required. */ 369 map->enable_feature (HB_TAG ('B','U','Z','Z')); /* Considered discretionary. */ 370 371 for (unsigned int i = 0; i < ARRAY_LENGTH (common_features); i++) 372 map->add_feature (common_features[i]); 373 374 if (HB_DIRECTION_IS_HORIZONTAL (planner->props.direction)) 375 for (unsigned int i = 0; i < ARRAY_LENGTH (horizontal_features); i++) 376 map->add_feature (horizontal_features[i]); 377 else 378 { 379 /* We only apply `vert` feature. See: 380 * https://github.com/harfbuzz/harfbuzz/commit/d71c0df2d17f4590d5611239577a6cb532c26528 381 * https://lists.freedesktop.org/archives/harfbuzz/2013-August/003490.html */ 382 383 /* We really want to find a 'vert' feature if there's any in the font, no 384 * matter which script/langsys it is listed (or not) under. 385 * See various bugs referenced from: 386 * https://github.com/harfbuzz/harfbuzz/issues/63 */ 387 map->enable_feature (HB_TAG ('v','e','r','t'), F_GLOBAL_SEARCH); 388 } 389 390 if (num_user_features) 391 map->is_simple = false; 392 for (unsigned int i = 0; i < num_user_features; i++) 393 { 394 const hb_feature_t *feature = &user_features[i]; 395 map->add_feature (feature->tag, 396 (feature->start == HB_FEATURE_GLOBAL_START && 397 feature->end == HB_FEATURE_GLOBAL_END) ? F_GLOBAL : F_NONE, 398 feature->value); 399 } 400 401 if (planner->shaper->override_features) 402 planner->shaper->override_features (planner); 403 } 404 405 406 /* 407 * shaper face data 408 */ 409 410 struct hb_ot_face_data_t {}; 411 412 hb_ot_face_data_t * 413 _hb_ot_shaper_face_data_create (hb_face_t *face) 414 { 415 return (hb_ot_face_data_t *) HB_SHAPER_DATA_SUCCEEDED; 416 } 417 418 void 419 _hb_ot_shaper_face_data_destroy (hb_ot_face_data_t *data) 420 { 421 } 422 423 424 /* 425 * shaper font data 426 */ 427 428 struct hb_ot_font_data_t { 429 OT::hb_scalar_cache_t unused; // Just for alignment 430 }; 431 432 hb_ot_font_data_t * 433 _hb_ot_shaper_font_data_create (hb_font_t *font) 434 { 435 const OT::ItemVariationStore &var_store = font->face->table.GDEF->table->get_var_store (); 436 return (hb_ot_font_data_t *) var_store.create_cache (); 437 } 438 439 void 440 _hb_ot_shaper_font_data_destroy (hb_ot_font_data_t *data) 441 { 442 OT::ItemVariationStore::destroy_cache ((OT::hb_scalar_cache_t *) data); 443 } 444 445 446 /* 447 * shaper 448 */ 449 450 struct hb_ot_shape_context_t 451 { 452 hb_ot_shape_plan_t *plan; 453 hb_font_t *font; 454 hb_face_t *face; 455 hb_buffer_t *buffer; 456 const hb_feature_t *user_features; 457 unsigned int num_user_features; 458 459 /* Transient stuff */ 460 hb_direction_t target_direction; 461 }; 462 463 464 465 /* Main shaper */ 466 467 468 /* Prepare */ 469 470 static void 471 hb_set_unicode_props (hb_buffer_t *buffer) 472 { 473 /* Implement enough of Unicode Graphemes here that shaping 474 * in reverse-direction wouldn't break graphemes. Namely, 475 * we mark all marks and ZWJ and ZWJ,Extended_Pictographic 476 * sequences as continuations. The foreach_grapheme() 477 * macro uses this bit. 478 * 479 * https://www.unicode.org/reports/tr29/#Regex_Definitions 480 */ 481 unsigned int count = buffer->len; 482 hb_glyph_info_t *info = buffer->info; 483 for (unsigned int i = 0; i < count; i++) 484 { 485 _hb_glyph_info_set_unicode_props (&info[i], buffer); 486 487 if (info[i].codepoint < 0x80) 488 continue; 489 490 unsigned gen_cat = _hb_glyph_info_get_general_category (&info[i]); 491 if (FLAG_UNSAFE (gen_cat) & 492 (FLAG (HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER) | 493 FLAG (HB_UNICODE_GENERAL_CATEGORY_UPPERCASE_LETTER) | 494 FLAG (HB_UNICODE_GENERAL_CATEGORY_TITLECASE_LETTER) | 495 FLAG (HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER) | 496 FLAG (HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR))) 497 continue; 498 499 /* Marks are already set as continuation by the above line. 500 * Handle Emoji_Modifier and ZWJ-continuation. */ 501 if (unlikely (gen_cat == HB_UNICODE_GENERAL_CATEGORY_MODIFIER_SYMBOL && 502 hb_in_range<hb_codepoint_t> (info[i].codepoint, 0x1F3FBu, 0x1F3FFu))) 503 { 504 _hb_glyph_info_set_continuation (&info[i], buffer); 505 } 506 /* Regional_Indicators are hairy as hell... 507 * https://github.com/harfbuzz/harfbuzz/issues/2265 */ 508 else if (unlikely (i && _hb_codepoint_is_regional_indicator (info[i].codepoint))) 509 { 510 if (_hb_codepoint_is_regional_indicator (info[i - 1].codepoint) && 511 !_hb_glyph_info_is_continuation (&info[i - 1])) 512 _hb_glyph_info_set_continuation (&info[i], buffer); 513 } 514 #ifndef HB_NO_EMOJI_SEQUENCES 515 else if (unlikely (_hb_glyph_info_is_zwj (&info[i]))) 516 { 517 _hb_glyph_info_set_continuation (&info[i], buffer); 518 if (i + 1 < count && 519 _hb_unicode_is_emoji_Extended_Pictographic (info[i + 1].codepoint)) 520 { 521 i++; 522 _hb_glyph_info_set_unicode_props (&info[i], buffer); 523 _hb_glyph_info_set_continuation (&info[i], buffer); 524 } 525 } 526 #endif 527 /* Or part of the Other_Grapheme_Extend that is not marks. 528 * As of Unicode 15 that is just: 529 * 530 * 200C ; Other_Grapheme_Extend # Cf ZERO WIDTH NON-JOINER 531 * FF9E..FF9F ; Other_Grapheme_Extend # Lm [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK 532 * E0020..E007F ; Other_Grapheme_Extend # Cf [96] TAG SPACE..CANCEL TAG 533 * 534 * ZWNJ is special, we don't want to merge it as there's no need, and keeping 535 * it separate results in more granular clusters. 536 * Tags are used for Emoji sub-region flag sequences: 537 * https://github.com/harfbuzz/harfbuzz/issues/1556 538 * Katakana ones were requested: 539 * https://github.com/harfbuzz/harfbuzz/issues/3844 540 */ 541 else if (unlikely (hb_in_ranges<hb_codepoint_t> (info[i].codepoint, 0xFF9Eu, 0xFF9Fu, 0xE0020u, 0xE007Fu))) 542 _hb_glyph_info_set_continuation (&info[i], buffer); 543 else if (unlikely (info[i].codepoint == 0x2044u /* FRACTION SLASH */)) 544 buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_FRACTION_SLASH; 545 } 546 } 547 548 static void 549 hb_insert_dotted_circle (hb_buffer_t *buffer, hb_font_t *font) 550 { 551 if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE)) 552 return; 553 554 if (!(buffer->flags & HB_BUFFER_FLAG_BOT) || 555 buffer->context_len[0] || 556 !_hb_glyph_info_is_unicode_mark (&buffer->info[0])) 557 return; 558 559 if (!font->has_glyph (0x25CCu)) 560 return; 561 562 hb_glyph_info_t dottedcircle = {0}; 563 dottedcircle.codepoint = 0x25CCu; 564 _hb_glyph_info_set_unicode_props (&dottedcircle, buffer); 565 566 buffer->clear_output (); 567 568 buffer->idx = 0; 569 hb_glyph_info_t info = dottedcircle; 570 info.cluster = buffer->cur().cluster; 571 info.mask = buffer->cur().mask; 572 (void) buffer->output_info (info); 573 574 buffer->sync (); 575 } 576 577 static void 578 hb_form_clusters (hb_buffer_t *buffer) 579 { 580 if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_CONTINUATIONS)) 581 return; 582 583 if (HB_BUFFER_CLUSTER_LEVEL_IS_GRAPHEMES (buffer->cluster_level)) 584 foreach_grapheme (buffer, start, end) 585 buffer->merge_clusters (start, end); 586 else 587 foreach_grapheme (buffer, start, end) 588 buffer->unsafe_to_break (start, end); 589 } 590 591 static void 592 hb_ensure_native_direction (hb_buffer_t *buffer) 593 { 594 hb_direction_t direction = buffer->props.direction; 595 hb_direction_t horiz_dir = hb_script_get_horizontal_direction (buffer->props.script); 596 597 /* Numeric runs in natively-RTL scripts are actually native-LTR, so we reset 598 * the horiz_dir if the run contains at least one decimal-number char, and no 599 * letter chars (ideally we should be checking for chars with strong 600 * directionality but hb-unicode currently lacks bidi categories). 601 * 602 * This allows digit sequences in Arabic etc to be shaped in "native" 603 * direction, so that features like ligatures will work as intended. 604 * 605 * https://github.com/harfbuzz/harfbuzz/issues/501 606 * 607 * Similar thing about Regional_Indicators; They are bidi=L, but Script=Common. 608 * If they are present in a run of natively-RTL text, they get assigned a script 609 * with natively RTL direction, which would result in wrong shaping if we 610 * assign such native RTL direction to them then. Detect that as well. 611 * 612 * https://github.com/harfbuzz/harfbuzz/issues/3314 613 */ 614 if (unlikely (horiz_dir == HB_DIRECTION_RTL && direction == HB_DIRECTION_LTR)) 615 { 616 bool found_number = false, found_letter = false, found_ri = false; 617 const auto* info = buffer->info; 618 const auto count = buffer->len; 619 for (unsigned i = 0; i < count; i++) 620 { 621 auto gc = _hb_glyph_info_get_general_category (&info[i]); 622 if (HB_UNICODE_GENERAL_CATEGORY_IS_LETTER (gc)) 623 { 624 found_letter = true; 625 break; 626 } 627 else if (gc == HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER) 628 found_number = true; 629 else if (unlikely (_hb_codepoint_is_regional_indicator (info[i].codepoint))) 630 found_ri = true; 631 } 632 if ((found_number || found_ri) && !found_letter) 633 horiz_dir = HB_DIRECTION_LTR; 634 } 635 636 /* TODO vertical: 637 * The only BTT vertical script is Ogham, but it's not clear to me whether OpenType 638 * Ogham fonts are supposed to be implemented BTT or not. Need to research that 639 * first. */ 640 if ((HB_DIRECTION_IS_HORIZONTAL (direction) && 641 direction != horiz_dir && HB_DIRECTION_IS_VALID (horiz_dir)) || 642 (HB_DIRECTION_IS_VERTICAL (direction) && 643 direction != HB_DIRECTION_TTB)) 644 { 645 _hb_ot_layout_reverse_graphemes (buffer); 646 buffer->props.direction = HB_DIRECTION_REVERSE (buffer->props.direction); 647 } 648 } 649 650 651 /* 652 * Substitute 653 */ 654 655 static inline void 656 hb_ot_rotate_chars (const hb_ot_shape_context_t *c) 657 { 658 hb_buffer_t *buffer = c->buffer; 659 unsigned int count = buffer->len; 660 hb_glyph_info_t *info = buffer->info; 661 662 if (HB_DIRECTION_IS_BACKWARD (c->target_direction)) 663 { 664 hb_unicode_funcs_t *unicode = buffer->unicode; 665 hb_mask_t rtlm_mask = c->plan->rtlm_mask; 666 667 for (unsigned int i = 0; i < count; i++) { 668 hb_codepoint_t codepoint = unicode->mirroring (info[i].codepoint); 669 if (unlikely (codepoint != info[i].codepoint && c->font->has_glyph (codepoint))) 670 info[i].codepoint = codepoint; 671 else 672 info[i].mask |= rtlm_mask; 673 } 674 } 675 676 #ifndef HB_NO_VERTICAL 677 if (HB_DIRECTION_IS_VERTICAL (c->target_direction) && !c->plan->has_vert) 678 { 679 for (unsigned int i = 0; i < count; i++) { 680 hb_codepoint_t codepoint = hb_unicode_funcs_t::vertical_char_for (info[i].codepoint); 681 if (unlikely (codepoint != info[i].codepoint && c->font->has_glyph (codepoint))) 682 info[i].codepoint = codepoint; 683 } 684 } 685 #endif 686 } 687 688 static inline void 689 hb_ot_shape_setup_masks_fraction (const hb_ot_shape_context_t *c) 690 { 691 #ifdef HB_NO_OT_SHAPE_FRACTIONS 692 return; 693 #endif 694 695 if (!(c->buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_FRACTION_SLASH) || 696 !c->plan->has_frac) 697 return; 698 699 hb_buffer_t *buffer = c->buffer; 700 701 hb_mask_t pre_mask, post_mask; 702 if (HB_DIRECTION_IS_FORWARD (buffer->props.direction)) 703 { 704 pre_mask = c->plan->numr_mask | c->plan->frac_mask; 705 post_mask = c->plan->frac_mask | c->plan->dnom_mask; 706 } 707 else 708 { 709 pre_mask = c->plan->frac_mask | c->plan->dnom_mask; 710 post_mask = c->plan->numr_mask | c->plan->frac_mask; 711 } 712 713 unsigned int count = buffer->len; 714 hb_glyph_info_t *info = buffer->info; 715 for (unsigned int i = 0; i < count; i++) 716 { 717 if (info[i].codepoint == 0x2044u) /* FRACTION SLASH */ 718 { 719 unsigned int start = i, end = i + 1; 720 while (start && 721 _hb_glyph_info_get_general_category (&info[start - 1]) == 722 HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER) 723 start--; 724 while (end < count && 725 _hb_glyph_info_get_general_category (&info[end]) == 726 HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER) 727 end++; 728 if (start == i || end == i + 1) 729 { 730 if (start == i) 731 buffer->unsafe_to_concat (start, start + 1); 732 if (end == i + 1) 733 buffer->unsafe_to_concat (end - 1, end); 734 continue; 735 } 736 737 buffer->unsafe_to_break (start, end); 738 739 for (unsigned int j = start; j < i; j++) 740 info[j].mask |= pre_mask; 741 info[i].mask |= c->plan->frac_mask; 742 for (unsigned int j = i + 1; j < end; j++) 743 info[j].mask |= post_mask; 744 745 i = end - 1; 746 } 747 } 748 } 749 750 static inline void 751 hb_ot_shape_initialize_masks (const hb_ot_shape_context_t *c) 752 { 753 hb_ot_map_t *map = &c->plan->map; 754 hb_buffer_t *buffer = c->buffer; 755 756 hb_mask_t global_mask = map->get_global_mask (); 757 buffer->reset_masks (global_mask); 758 } 759 760 static inline void 761 hb_ot_shape_setup_masks (const hb_ot_shape_context_t *c) 762 { 763 hb_ot_map_t *map = &c->plan->map; 764 hb_buffer_t *buffer = c->buffer; 765 766 hb_ot_shape_setup_masks_fraction (c); 767 768 if (c->plan->shaper->setup_masks) 769 c->plan->shaper->setup_masks (c->plan, buffer, c->font); 770 771 for (unsigned int i = 0; i < c->num_user_features; i++) 772 { 773 const hb_feature_t *feature = &c->user_features[i]; 774 if (!(feature->start == HB_FEATURE_GLOBAL_START && feature->end == HB_FEATURE_GLOBAL_END)) { 775 unsigned int shift; 776 hb_mask_t mask = map->get_mask (feature->tag, &shift); 777 buffer->set_masks (feature->value << shift, mask, feature->start, feature->end); 778 } 779 } 780 } 781 782 static void 783 hb_ot_zero_width_default_ignorables (const hb_buffer_t *buffer) 784 { 785 if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES) || 786 (buffer->flags & HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES) || 787 (buffer->flags & HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES)) 788 return; 789 790 unsigned int count = buffer->len; 791 hb_glyph_info_t *info = buffer->info; 792 hb_glyph_position_t *pos = buffer->pos; 793 unsigned int i = 0; 794 for (i = 0; i < count; i++) 795 if (unlikely (_hb_glyph_info_is_default_ignorable (&info[i]))) 796 { 797 pos[i].x_advance = pos[i].y_advance = 0; 798 if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction)) 799 pos[i].x_offset = 0; 800 else 801 pos[i].y_offset = 0; 802 } 803 } 804 805 static void 806 hb_ot_deal_with_variation_selectors (hb_buffer_t *buffer) 807 { 808 if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_VARIATION_SELECTOR_FALLBACK) || 809 buffer->not_found_variation_selector == HB_CODEPOINT_INVALID) 810 return; 811 812 unsigned int count = buffer->len; 813 hb_glyph_info_t *info = buffer->info; 814 hb_glyph_position_t *pos = buffer->pos; 815 816 for (unsigned int i = 0; i < count; i++) 817 { 818 if (_hb_glyph_info_is_variation_selector (&info[i])) 819 { 820 info[i].codepoint = buffer->not_found_variation_selector; 821 pos[i].x_advance = pos[i].y_advance = pos[i].x_offset = pos[i].y_offset = 0; 822 _hb_glyph_info_set_variation_selector (&info[i], false); 823 } 824 } 825 } 826 827 static void 828 hb_ot_hide_default_ignorables (hb_buffer_t *buffer, 829 hb_font_t *font) 830 { 831 if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES) || 832 (buffer->flags & HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES)) 833 return; 834 835 unsigned int count = buffer->len; 836 hb_glyph_info_t *info = buffer->info; 837 838 hb_codepoint_t invisible = buffer->invisible; 839 if (!(buffer->flags & HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES) && 840 (invisible || font->get_nominal_glyph (' ', &invisible))) 841 { 842 /* Replace default-ignorables with a zero-advance invisible glyph. */ 843 for (unsigned int i = 0; i < count; i++) 844 { 845 if (_hb_glyph_info_is_default_ignorable (&info[i])) 846 info[i].codepoint = invisible; 847 } 848 } 849 else 850 buffer->delete_glyphs_inplace (_hb_glyph_info_is_default_ignorable); 851 } 852 853 854 static inline void 855 hb_ot_map_glyphs_fast (hb_buffer_t *buffer) 856 { 857 /* Normalization process sets up normalizer_glyph_index(), we just copy it. */ 858 unsigned int count = buffer->len; 859 hb_glyph_info_t *info = buffer->info; 860 for (unsigned int i = 0; i < count; i++) 861 info[i].codepoint = info[i].normalizer_glyph_index(); 862 863 buffer->content_type = HB_BUFFER_CONTENT_TYPE_GLYPHS; 864 } 865 866 static inline void 867 hb_synthesize_glyph_classes (hb_buffer_t *buffer) 868 { 869 unsigned int count = buffer->len; 870 hb_glyph_info_t *info = buffer->info; 871 for (unsigned int i = 0; i < count; i++) 872 { 873 hb_ot_layout_glyph_props_flags_t klass; 874 875 /* Never mark default-ignorables as marks. 876 * They won't get in the way of lookups anyway, 877 * but having them as mark will cause them to be skipped 878 * over if the lookup-flag says so, but at least for the 879 * Mongolian variation selectors, looks like Uniscribe 880 * marks them as non-mark. Some Mongolian fonts without 881 * GDEF rely on this. Another notable character that 882 * this applies to is COMBINING GRAPHEME JOINER. */ 883 klass = (_hb_glyph_info_get_general_category (&info[i]) != 884 HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK || 885 _hb_glyph_info_is_default_ignorable (&info[i])) ? 886 HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH : 887 HB_OT_LAYOUT_GLYPH_PROPS_MARK; 888 _hb_glyph_info_set_glyph_props (&info[i], klass); 889 } 890 } 891 892 static inline void 893 hb_ot_substitute_default (const hb_ot_shape_context_t *c) 894 { 895 hb_buffer_t *buffer = c->buffer; 896 897 hb_ot_rotate_chars (c); 898 899 HB_BUFFER_ALLOCATE_VAR (buffer, normalizer_glyph_index); 900 901 _hb_ot_shape_normalize (c->plan, buffer, c->font); 902 903 hb_ot_shape_setup_masks (c); 904 905 /* This is unfortunate to go here, but necessary... */ 906 if (c->plan->fallback_mark_positioning) 907 _hb_ot_shape_fallback_mark_position_recategorize_marks (c->plan, c->font, buffer); 908 909 hb_ot_map_glyphs_fast (buffer); 910 911 HB_BUFFER_DEALLOCATE_VAR (buffer, normalizer_glyph_index); 912 } 913 914 static inline void 915 hb_ot_substitute_plan (const hb_ot_shape_context_t *c) 916 { 917 hb_buffer_t *buffer = c->buffer; 918 919 hb_ot_layout_substitute_start (c->font, buffer); 920 921 if (c->plan->fallback_glyph_classes) 922 hb_synthesize_glyph_classes (c->buffer); 923 924 #ifndef HB_NO_AAT_SHAPE 925 if (unlikely (c->plan->apply_morx)) 926 { 927 hb_aat_layout_substitute (c->plan, c->font, c->buffer, 928 c->user_features, c->num_user_features); 929 c->buffer->update_digest (); 930 } 931 else 932 #endif 933 { 934 c->buffer->update_digest (); 935 c->plan->substitute (c->font, buffer); 936 } 937 } 938 939 static inline void 940 hb_ot_substitute_pre (const hb_ot_shape_context_t *c) 941 { 942 hb_ot_substitute_default (c); 943 944 _hb_buffer_allocate_gsubgpos_vars (c->buffer); 945 946 hb_ot_substitute_plan (c); 947 948 #ifndef HB_NO_AAT_SHAPE 949 if (c->plan->apply_morx && c->plan->apply_gpos) 950 hb_aat_layout_remove_deleted_glyphs (c->buffer); 951 #endif 952 } 953 954 static inline void 955 hb_ot_substitute_post (const hb_ot_shape_context_t *c) 956 { 957 #ifndef HB_NO_AAT_SHAPE 958 if (c->plan->apply_morx && !c->plan->apply_gpos) 959 hb_aat_layout_remove_deleted_glyphs (c->buffer); 960 #endif 961 962 hb_ot_deal_with_variation_selectors (c->buffer); 963 hb_ot_hide_default_ignorables (c->buffer, c->font); 964 965 if (c->plan->shaper->postprocess_glyphs && 966 c->buffer->message(c->font, "start postprocess-glyphs")) { 967 c->plan->shaper->postprocess_glyphs (c->plan, c->buffer, c->font); 968 (void) c->buffer->message(c->font, "end postprocess-glyphs"); 969 } 970 } 971 972 973 /* 974 * Position 975 */ 976 977 static inline void 978 adjust_mark_offsets (hb_glyph_position_t *pos) 979 { 980 pos->x_offset -= pos->x_advance; 981 pos->y_offset -= pos->y_advance; 982 } 983 984 static inline void 985 zero_mark_width (hb_glyph_position_t *pos) 986 { 987 pos->x_advance = 0; 988 pos->y_advance = 0; 989 } 990 991 static inline void 992 zero_mark_widths_by_gdef (hb_buffer_t *buffer, bool adjust_offsets) 993 { 994 unsigned int count = buffer->len; 995 hb_glyph_info_t *info = buffer->info; 996 for (unsigned int i = 0; i < count; i++) 997 if (_hb_glyph_info_is_mark (&info[i])) 998 { 999 if (adjust_offsets) 1000 adjust_mark_offsets (&buffer->pos[i]); 1001 zero_mark_width (&buffer->pos[i]); 1002 } 1003 } 1004 1005 static inline void 1006 hb_ot_position_default (const hb_ot_shape_context_t *c) 1007 { 1008 hb_direction_t direction = c->buffer->props.direction; 1009 unsigned int count = c->buffer->len; 1010 hb_glyph_info_t *info = c->buffer->info; 1011 hb_glyph_position_t *pos = c->buffer->pos; 1012 1013 if (HB_DIRECTION_IS_HORIZONTAL (direction)) 1014 { 1015 c->font->get_glyph_h_advances (count, &info[0].codepoint, sizeof(info[0]), 1016 &pos[0].x_advance, sizeof(pos[0])); 1017 // h_origin defaults to zero; only apply it if the font has it. 1018 if (c->font->has_glyph_h_origin_func () || c->font->has_glyph_h_origins_func ()) 1019 c->font->subtract_glyph_h_origins (c->buffer); 1020 } 1021 else 1022 { 1023 c->font->get_glyph_v_advances (count, &info[0].codepoint, sizeof(info[0]), 1024 &pos[0].y_advance, sizeof(pos[0])); 1025 // v_origin defaults to non-zero; apply even if only fallback is there. 1026 c->font->subtract_glyph_v_origins (c->buffer); 1027 } 1028 if (c->buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_SPACE_FALLBACK) 1029 _hb_ot_shape_fallback_spaces (c->plan, c->font, c->buffer); 1030 } 1031 1032 static inline void 1033 hb_ot_position_plan (const hb_ot_shape_context_t *c) 1034 { 1035 /* If the font has no GPOS and direction is forward, then when 1036 * zeroing mark widths, we shift the mark with it, such that the 1037 * mark is positioned hanging over the previous glyph. When 1038 * direction is backward we don't shift and it will end up 1039 * hanging over the next glyph after the final reordering. 1040 * 1041 * Note: If fallback positioning happens, we don't care about 1042 * this as it will be overridden. 1043 */ 1044 bool adjust_offsets_when_zeroing = c->plan->adjust_mark_positioning_when_zeroing && 1045 HB_DIRECTION_IS_FORWARD (c->buffer->props.direction); 1046 1047 /* We change glyph origin to what GPOS expects (horizontal), apply GPOS, change it back. */ 1048 1049 // h_origin defaults to zero; only apply it if the font has it. 1050 if (c->font->has_glyph_h_origin_func () || c->font->has_glyph_h_origins_func ()) 1051 c->font->add_glyph_h_origins (c->buffer); 1052 1053 hb_ot_layout_position_start (c->font, c->buffer); 1054 1055 if (c->plan->zero_marks) 1056 switch (c->plan->shaper->zero_width_marks) 1057 { 1058 case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY: 1059 zero_mark_widths_by_gdef (c->buffer, adjust_offsets_when_zeroing); 1060 break; 1061 1062 default: 1063 case HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE: 1064 case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE: 1065 break; 1066 } 1067 1068 c->plan->position (c->font, c->buffer); 1069 1070 if (c->plan->zero_marks) 1071 switch (c->plan->shaper->zero_width_marks) 1072 { 1073 case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE: 1074 zero_mark_widths_by_gdef (c->buffer, adjust_offsets_when_zeroing); 1075 break; 1076 1077 default: 1078 case HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE: 1079 case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY: 1080 break; 1081 } 1082 1083 /* Finish off. Has to follow a certain order. */ 1084 hb_ot_layout_position_finish_advances (c->font, c->buffer); 1085 hb_ot_zero_width_default_ignorables (c->buffer); 1086 hb_ot_layout_position_finish_offsets (c->font, c->buffer); 1087 1088 // h_origin defaults to zero; only apply it if the font has it. 1089 if (c->font->has_glyph_h_origin_func () || c->font->has_glyph_h_origins_func ()) 1090 c->font->subtract_glyph_h_origins (c->buffer); 1091 1092 if (c->plan->fallback_mark_positioning) 1093 _hb_ot_shape_fallback_mark_position (c->plan, c->font, c->buffer, 1094 adjust_offsets_when_zeroing); 1095 } 1096 1097 static inline void 1098 hb_ot_position (const hb_ot_shape_context_t *c) 1099 { 1100 c->buffer->clear_positions (); 1101 1102 hb_ot_position_default (c); 1103 1104 hb_ot_position_plan (c); 1105 1106 if (HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction)) 1107 hb_buffer_reverse (c->buffer); 1108 1109 _hb_buffer_deallocate_gsubgpos_vars (c->buffer); 1110 } 1111 1112 static inline void 1113 hb_propagate_flags (hb_buffer_t *buffer) 1114 { 1115 /* Propagate cluster-level glyph flags to be the same on all cluster glyphs. 1116 * Simplifies using them. */ 1117 1118 hb_mask_t and_mask = HB_GLYPH_FLAG_DEFINED; 1119 if ((buffer->flags & HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT) == 0) 1120 and_mask &= ~HB_GLYPH_FLAG_UNSAFE_TO_CONCAT; 1121 1122 hb_glyph_info_t *info = buffer->info; 1123 1124 if ((buffer->flags & HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_TATWEEL) == 0) 1125 { 1126 foreach_cluster (buffer, start, end) 1127 { 1128 if (end - start == 1) 1129 { 1130 info[start].mask &= and_mask; 1131 continue; 1132 } 1133 1134 unsigned int mask = 0; 1135 for (unsigned int i = start; i < end; i++) 1136 mask |= info[i].mask; 1137 1138 mask &= and_mask; 1139 1140 for (unsigned int i = start; i < end; i++) 1141 info[i].mask = mask; 1142 } 1143 return; 1144 } 1145 1146 /* If we are producing SAFE_TO_INSERT_TATWEEL, then do two things: 1147 * 1148 * - If the places that the Arabic shaper marked as SAFE_TO_INSERT_TATWEEL, 1149 * are UNSAFE_TO_BREAK, then clear the SAFE_TO_INSERT_TATWEEL, 1150 * - Any place that is SAFE_TO_INSERT_TATWEEL, is also now UNSAFE_TO_BREAK. 1151 * 1152 * We couldn't make this interaction earlier. It has to be done this way. 1153 */ 1154 foreach_cluster (buffer, start, end) 1155 { 1156 unsigned int mask = 0; 1157 for (unsigned int i = start; i < end; i++) 1158 mask |= info[i].mask; 1159 1160 if (mask & HB_GLYPH_FLAG_UNSAFE_TO_BREAK) 1161 mask &= ~HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL; 1162 if (mask & HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL) 1163 mask |= HB_GLYPH_FLAG_UNSAFE_TO_BREAK | HB_GLYPH_FLAG_UNSAFE_TO_CONCAT; 1164 1165 mask &= and_mask; 1166 1167 for (unsigned int i = start; i < end; i++) 1168 info[i].mask = mask; 1169 } 1170 } 1171 1172 /* Pull it all together! */ 1173 1174 static void 1175 hb_ot_shape_internal (hb_ot_shape_context_t *c) 1176 { 1177 /* Save the original direction, we use it later. */ 1178 c->target_direction = c->buffer->props.direction; 1179 1180 _hb_buffer_allocate_unicode_vars (c->buffer); 1181 1182 hb_ot_shape_initialize_masks (c); 1183 hb_set_unicode_props (c->buffer); 1184 hb_insert_dotted_circle (c->buffer, c->font); 1185 1186 hb_form_clusters (c->buffer); 1187 1188 hb_ensure_native_direction (c->buffer); 1189 1190 if (c->plan->shaper->preprocess_text && 1191 c->buffer->message(c->font, "start preprocess-text")) 1192 { 1193 c->plan->shaper->preprocess_text (c->plan, c->buffer, c->font); 1194 (void) c->buffer->message(c->font, "end preprocess-text"); 1195 } 1196 1197 hb_ot_substitute_pre (c); 1198 hb_ot_position (c); 1199 hb_ot_substitute_post (c); 1200 1201 hb_propagate_flags (c->buffer); 1202 1203 _hb_buffer_deallocate_unicode_vars (c->buffer); 1204 1205 c->buffer->props.direction = c->target_direction; 1206 } 1207 1208 1209 hb_bool_t 1210 _hb_ot_shape (hb_shape_plan_t *shape_plan, 1211 hb_font_t *font, 1212 hb_buffer_t *buffer, 1213 const hb_feature_t *features, 1214 unsigned int num_features) 1215 { 1216 hb_ot_shape_context_t c = {&shape_plan->ot, font, font->face, buffer, features, num_features}; 1217 hb_ot_shape_internal (&c); 1218 1219 return true; 1220 } 1221 1222 1223 /** 1224 * hb_ot_shape_plan_collect_lookups: 1225 * @shape_plan: #hb_shape_plan_t to query 1226 * @table_tag: GSUB or GPOS 1227 * @lookup_indexes: (out): The #hb_set_t set of lookups returned 1228 * 1229 * Computes the complete set of GSUB or GPOS lookups that are applicable 1230 * under a given @shape_plan. 1231 * 1232 * Since: 0.9.7 1233 **/ 1234 void 1235 hb_ot_shape_plan_collect_lookups (hb_shape_plan_t *shape_plan, 1236 hb_tag_t table_tag, 1237 hb_set_t *lookup_indexes /* OUT */) 1238 { 1239 shape_plan->ot.collect_lookups (table_tag, lookup_indexes); 1240 } 1241 1242 1243 /** 1244 * hb_ot_shape_plan_get_feature_tags: 1245 * @shape_plan: A shaping plan 1246 * @start_offset: The index of first feature to retrieve 1247 * @tag_count: (inout): Input = the maximum number of features to return; 1248 * Output = the actual number of features returned (may be zero) 1249 * @tags: (out) (array length=tag_count): The array of enabled feature 1250 * 1251 * Fetches the list of OpenType feature tags enabled for a shaping plan, if possible. 1252 * 1253 * Return value: Total number of feature tagss. 1254 * 1255 * Since: 10.3.0 1256 */ 1257 unsigned int 1258 hb_ot_shape_plan_get_feature_tags (hb_shape_plan_t *shape_plan, 1259 unsigned int start_offset, 1260 unsigned int *tag_count, /* IN/OUT */ 1261 hb_tag_t *tags /* OUT */) 1262 { 1263 #ifndef HB_NO_OT_SHAPE 1264 return shape_plan->ot.map.get_feature_tags (start_offset, tag_count, tags); 1265 #else 1266 if (tag_count) 1267 *tag_count = 0; 1268 return 0; 1269 #endif 1270 } 1271 1272 1273 /* TODO Move this to hb-ot-shape-normalize, make it do decompose, and make it public. */ 1274 static void 1275 add_char (hb_font_t *font, 1276 hb_unicode_funcs_t *unicode, 1277 hb_bool_t mirror, 1278 hb_codepoint_t u, 1279 hb_set_t *glyphs) 1280 { 1281 hb_codepoint_t glyph; 1282 if (font->get_nominal_glyph (u, &glyph)) 1283 glyphs->add (glyph); 1284 if (mirror) 1285 { 1286 hb_codepoint_t m = unicode->mirroring (u); 1287 if (m != u && font->get_nominal_glyph (m, &glyph)) 1288 glyphs->add (glyph); 1289 } 1290 } 1291 1292 1293 /** 1294 * hb_ot_shape_glyphs_closure: 1295 * @font: #hb_font_t to work upon 1296 * @buffer: The input buffer to compute from 1297 * @features: (array length=num_features): The features enabled on the buffer 1298 * @num_features: The number of features enabled on the buffer 1299 * @glyphs: (out): The #hb_set_t set of glyphs comprising the transitive closure of the query 1300 * 1301 * Computes the transitive closure of glyphs needed for a specified 1302 * input buffer under the given font and feature list. The closure is 1303 * computed as a set, not as a list. 1304 * 1305 * Since: 0.9.2 1306 **/ 1307 void 1308 hb_ot_shape_glyphs_closure (hb_font_t *font, 1309 hb_buffer_t *buffer, 1310 const hb_feature_t *features, 1311 unsigned int num_features, 1312 hb_set_t *glyphs) 1313 { 1314 const char *shapers[] = {"ot", nullptr}; 1315 hb_shape_plan_t *shape_plan = hb_shape_plan_create_cached (font->face, &buffer->props, 1316 features, num_features, shapers); 1317 1318 bool mirror = hb_script_get_horizontal_direction (buffer->props.script) == HB_DIRECTION_RTL; 1319 1320 unsigned int count = buffer->len; 1321 hb_glyph_info_t *info = buffer->info; 1322 for (unsigned int i = 0; i < count; i++) 1323 add_char (font, buffer->unicode, mirror, info[i].codepoint, glyphs); 1324 1325 hb_set_t *lookups = hb_set_create (); 1326 hb_ot_shape_plan_collect_lookups (shape_plan, HB_OT_TAG_GSUB, lookups); 1327 hb_ot_layout_lookups_substitute_closure (font->face, lookups, glyphs); 1328 1329 hb_set_destroy (lookups); 1330 1331 hb_shape_plan_destroy (shape_plan); 1332 } 1333 1334 1335 #endif