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 #define HB_SHAPER ot 30 #define hb_ot_shaper_face_data_t hb_ot_layout_t 31 #define hb_ot_shaper_shape_plan_data_t hb_ot_shape_plan_t 32 #include "hb-shaper-impl-private.hh" 33 34 #include "hb-ot-shape-private.hh" 35 #include "hb-ot-shape-complex-private.hh" 36 #include "hb-ot-shape-fallback-private.hh" 37 #include "hb-ot-shape-normalize-private.hh" 38 39 #include "hb-ot-layout-private.hh" 40 #include "hb-set-private.hh" 41 42 43 static hb_tag_t common_features[] = { 44 HB_TAG('c','c','m','p'), 45 HB_TAG('l','i','g','a'), 46 HB_TAG('l','o','c','l'), 47 HB_TAG('m','a','r','k'), 48 HB_TAG('m','k','m','k'), 49 HB_TAG('r','l','i','g'), 50 }; 51 52 53 static hb_tag_t horizontal_features[] = { 54 HB_TAG('c','a','l','t'), 55 HB_TAG('c','l','i','g'), 56 HB_TAG('c','u','r','s'), 57 HB_TAG('k','e','r','n'), 58 HB_TAG('r','c','l','t'), 59 }; 60 61 static hb_tag_t vertical_features[] = { 62 HB_TAG('v','e','r','t'), 63 }; 64 65 66 67 static void 68 hb_ot_shape_collect_features (hb_ot_shape_planner_t *planner, 69 const hb_segment_properties_t *props, 70 const hb_feature_t *user_features, 71 unsigned int num_user_features) 72 { 73 hb_ot_map_builder_t *map = &planner->map; 74 75 switch (props->direction) { 76 case HB_DIRECTION_LTR: 77 map->add_global_bool_feature (HB_TAG ('l','t','r','a')); 78 map->add_global_bool_feature (HB_TAG ('l','t','r','m')); 79 break; 80 case HB_DIRECTION_RTL: 81 map->add_global_bool_feature (HB_TAG ('r','t','l','a')); 82 map->add_feature (HB_TAG ('r','t','l','m'), 1, F_NONE); 83 break; 84 case HB_DIRECTION_TTB: 85 case HB_DIRECTION_BTT: 86 case HB_DIRECTION_INVALID: 87 default: 88 break; 89 } 90 91 map->add_feature (HB_TAG ('f','r','a','c'), 1, F_NONE); 92 map->add_feature (HB_TAG ('n','u','m','r'), 1, F_NONE); 93 map->add_feature (HB_TAG ('d','n','o','m'), 1, F_NONE); 94 95 if (planner->shaper->collect_features) 96 planner->shaper->collect_features (planner); 97 98 for (unsigned int i = 0; i < ARRAY_LENGTH (common_features); i++) 99 map->add_global_bool_feature (common_features[i]); 100 101 if (HB_DIRECTION_IS_HORIZONTAL (props->direction)) 102 for (unsigned int i = 0; i < ARRAY_LENGTH (horizontal_features); i++) 103 map->add_feature (horizontal_features[i], 1, F_GLOBAL | 104 (horizontal_features[i] == HB_TAG('k','e','r','n') ? 105 F_HAS_FALLBACK : F_NONE)); 106 else 107 for (unsigned int i = 0; i < ARRAY_LENGTH (vertical_features); i++) 108 map->add_feature (vertical_features[i], 1, F_GLOBAL | 109 (vertical_features[i] == HB_TAG('v','k','r','n') ? 110 F_HAS_FALLBACK : F_NONE)); 111 112 if (planner->shaper->override_features) 113 planner->shaper->override_features (planner); 114 115 for (unsigned int i = 0; i < num_user_features; i++) { 116 const hb_feature_t *feature = &user_features[i]; 117 map->add_feature (feature->tag, feature->value, 118 (feature->start == 0 && feature->end == (unsigned int) -1) ? 119 F_GLOBAL : F_NONE); 120 } 121 } 122 123 124 /* 125 * shaper face data 126 */ 127 128 hb_ot_shaper_face_data_t * 129 _hb_ot_shaper_face_data_create (hb_face_t *face) 130 { 131 return _hb_ot_layout_create (face); 132 } 133 134 void 135 _hb_ot_shaper_face_data_destroy (hb_ot_shaper_face_data_t *data) 136 { 137 _hb_ot_layout_destroy (data); 138 } 139 140 141 /* 142 * shaper font data 143 */ 144 145 struct hb_ot_shaper_font_data_t {}; 146 147 hb_ot_shaper_font_data_t * 148 _hb_ot_shaper_font_data_create (hb_font_t *font) 149 { 150 return (hb_ot_shaper_font_data_t *) HB_SHAPER_DATA_SUCCEEDED; 151 } 152 153 void 154 _hb_ot_shaper_font_data_destroy (hb_ot_shaper_font_data_t *data) 155 { 156 } 157 158 159 /* 160 * shaper shape_plan data 161 */ 162 163 hb_ot_shaper_shape_plan_data_t * 164 _hb_ot_shaper_shape_plan_data_create (hb_shape_plan_t *shape_plan, 165 const hb_feature_t *user_features, 166 unsigned int num_user_features) 167 { 168 hb_ot_shape_plan_t *plan = (hb_ot_shape_plan_t *) calloc (1, sizeof (hb_ot_shape_plan_t)); 169 if (unlikely (!plan)) 170 return NULL; 171 172 hb_ot_shape_planner_t planner (shape_plan); 173 174 planner.shaper = hb_ot_shape_complex_categorize (&planner); 175 176 hb_ot_shape_collect_features (&planner, &shape_plan->props, user_features, num_user_features); 177 178 planner.compile (*plan); 179 180 if (plan->shaper->data_create) { 181 plan->data = plan->shaper->data_create (plan); 182 if (unlikely (!plan->data)) 183 return NULL; 184 } 185 186 return plan; 187 } 188 189 void 190 _hb_ot_shaper_shape_plan_data_destroy (hb_ot_shaper_shape_plan_data_t *plan) 191 { 192 if (plan->shaper->data_destroy) 193 plan->shaper->data_destroy (const_cast<void *> (plan->data)); 194 195 plan->finish (); 196 197 free (plan); 198 } 199 200 201 /* 202 * shaper 203 */ 204 205 struct hb_ot_shape_context_t 206 { 207 hb_ot_shape_plan_t *plan; 208 hb_font_t *font; 209 hb_face_t *face; 210 hb_buffer_t *buffer; 211 const hb_feature_t *user_features; 212 unsigned int num_user_features; 213 214 /* Transient stuff */ 215 hb_direction_t target_direction; 216 }; 217 218 219 220 /* Main shaper */ 221 222 223 /* Prepare */ 224 225 static void 226 hb_set_unicode_props (hb_buffer_t *buffer) 227 { 228 unsigned int count = buffer->len; 229 for (unsigned int i = 0; i < count; i++) 230 _hb_glyph_info_set_unicode_props (&buffer->info[i], buffer->unicode); 231 } 232 233 static void 234 hb_insert_dotted_circle (hb_buffer_t *buffer, hb_font_t *font) 235 { 236 if (!(buffer->flags & HB_BUFFER_FLAG_BOT) || 237 _hb_glyph_info_get_general_category (&buffer->info[0]) != 238 HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) 239 return; 240 241 if (!font->has_glyph (0x25CC)) 242 return; 243 244 hb_glyph_info_t dottedcircle; 245 dottedcircle.codepoint = 0x25CC; 246 _hb_glyph_info_set_unicode_props (&dottedcircle, buffer->unicode); 247 248 buffer->clear_output (); 249 250 buffer->idx = 0; 251 hb_glyph_info_t info = dottedcircle; 252 info.cluster = buffer->cur().cluster; 253 info.mask = buffer->cur().mask; 254 buffer->output_info (info); 255 while (buffer->idx < buffer->len) 256 buffer->next_glyph (); 257 258 buffer->swap_buffers (); 259 } 260 261 static void 262 hb_form_clusters (hb_buffer_t *buffer) 263 { 264 unsigned int count = buffer->len; 265 for (unsigned int i = 1; i < count; i++) 266 if (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&buffer->info[i]))) 267 buffer->merge_clusters (i - 1, i + 1); 268 } 269 270 static void 271 hb_ensure_native_direction (hb_buffer_t *buffer) 272 { 273 hb_direction_t direction = buffer->props.direction; 274 275 /* TODO vertical: 276 * The only BTT vertical script is Ogham, but it's not clear to me whether OpenType 277 * Ogham fonts are supposed to be implemented BTT or not. Need to research that 278 * first. */ 279 if ((HB_DIRECTION_IS_HORIZONTAL (direction) && direction != hb_script_get_horizontal_direction (buffer->props.script)) || 280 (HB_DIRECTION_IS_VERTICAL (direction) && direction != HB_DIRECTION_TTB)) 281 { 282 hb_buffer_reverse_clusters (buffer); 283 buffer->props.direction = HB_DIRECTION_REVERSE (buffer->props.direction); 284 } 285 } 286 287 288 /* Substitute */ 289 290 static inline void 291 hb_ot_mirror_chars (hb_ot_shape_context_t *c) 292 { 293 if (HB_DIRECTION_IS_FORWARD (c->target_direction)) 294 return; 295 296 hb_buffer_t *buffer = c->buffer; 297 hb_unicode_funcs_t *unicode = buffer->unicode; 298 hb_mask_t rtlm_mask = c->plan->rtlm_mask; 299 300 unsigned int count = buffer->len; 301 hb_glyph_info_t *info = buffer->info; 302 for (unsigned int i = 0; i < count; i++) { 303 hb_codepoint_t codepoint = unicode->mirroring (info[i].codepoint); 304 if (likely (codepoint == info[i].codepoint)) 305 info[i].mask |= rtlm_mask; 306 else 307 info[i].codepoint = codepoint; 308 } 309 } 310 311 static inline void 312 hb_ot_shape_setup_masks_fraction (hb_ot_shape_context_t *c) 313 { 314 if (!c->plan->has_frac) 315 return; 316 317 hb_buffer_t *buffer = c->buffer; 318 319 /* TODO look in pre/post context text also. */ 320 unsigned int count = buffer->len; 321 hb_glyph_info_t *info = buffer->info; 322 for (unsigned int i = 0; i < count; i++) 323 { 324 if (info[i].codepoint == 0x2044) /* FRACTION SLASH */ 325 { 326 unsigned int start = i, end = i + 1; 327 while (start && 328 _hb_glyph_info_get_general_category (&info[start - 1]) == 329 HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER) 330 start--; 331 while (end < count && 332 _hb_glyph_info_get_general_category (&info[end]) == 333 HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER) 334 end++; 335 336 for (unsigned int j = start; j < i; j++) 337 info[j].mask |= c->plan->numr_mask | c->plan->frac_mask; 338 info[i].mask |= c->plan->frac_mask; 339 for (unsigned int j = i + 1; j < end; j++) 340 info[j].mask |= c->plan->frac_mask | c->plan->dnom_mask; 341 342 i = end - 1; 343 } 344 } 345 } 346 347 static inline void 348 hb_ot_shape_initialize_masks (hb_ot_shape_context_t *c) 349 { 350 hb_ot_map_t *map = &c->plan->map; 351 hb_buffer_t *buffer = c->buffer; 352 353 hb_mask_t global_mask = map->get_global_mask (); 354 buffer->reset_masks (global_mask); 355 } 356 357 static inline void 358 hb_ot_shape_setup_masks (hb_ot_shape_context_t *c) 359 { 360 hb_ot_map_t *map = &c->plan->map; 361 hb_buffer_t *buffer = c->buffer; 362 363 hb_ot_shape_setup_masks_fraction (c); 364 365 if (c->plan->shaper->setup_masks) 366 c->plan->shaper->setup_masks (c->plan, buffer, c->font); 367 368 for (unsigned int i = 0; i < c->num_user_features; i++) 369 { 370 const hb_feature_t *feature = &c->user_features[i]; 371 if (!(feature->start == 0 && feature->end == (unsigned int)-1)) { 372 unsigned int shift; 373 hb_mask_t mask = map->get_mask (feature->tag, &shift); 374 buffer->set_masks (feature->value << shift, mask, feature->start, feature->end); 375 } 376 } 377 } 378 379 static inline void 380 hb_ot_map_glyphs_fast (hb_buffer_t *buffer) 381 { 382 /* Normalization process sets up glyph_index(), we just copy it. */ 383 unsigned int count = buffer->len; 384 for (unsigned int i = 0; i < count; i++) 385 buffer->info[i].codepoint = buffer->info[i].glyph_index(); 386 } 387 388 static inline void 389 hb_synthesize_glyph_classes (hb_ot_shape_context_t *c) 390 { 391 unsigned int count = c->buffer->len; 392 hb_glyph_info_t *info = c->buffer->info; 393 for (unsigned int i = 0; i < count; i++) 394 _hb_glyph_info_set_glyph_props (&info[i], 395 _hb_glyph_info_get_general_category (&info[i]) 396 == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK ? 397 HB_OT_LAYOUT_GLYPH_PROPS_MARK : 398 HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH); 399 } 400 401 static inline void 402 hb_ot_substitute_default (hb_ot_shape_context_t *c) 403 { 404 hb_buffer_t *buffer = c->buffer; 405 406 if (c->plan->shaper->preprocess_text) 407 c->plan->shaper->preprocess_text (c->plan, buffer, c->font); 408 409 hb_ot_shape_initialize_masks (c); 410 411 hb_ot_mirror_chars (c); 412 413 HB_BUFFER_ALLOCATE_VAR (buffer, glyph_index); 414 415 _hb_ot_shape_normalize (c->plan, buffer, c->font); 416 417 hb_ot_shape_setup_masks (c); 418 419 /* This is unfortunate to go here, but necessary... */ 420 if (!hb_ot_layout_has_positioning (c->face)) 421 _hb_ot_shape_fallback_position_recategorize_marks (c->plan, c->font, buffer); 422 423 hb_ot_map_glyphs_fast (buffer); 424 425 HB_BUFFER_DEALLOCATE_VAR (buffer, glyph_index); 426 } 427 428 static inline void 429 hb_ot_substitute_complex (hb_ot_shape_context_t *c) 430 { 431 hb_buffer_t *buffer = c->buffer; 432 433 hb_ot_layout_substitute_start (c->font, buffer); 434 435 if (!hb_ot_layout_has_glyph_classes (c->face)) 436 hb_synthesize_glyph_classes (c); 437 438 c->plan->substitute (c->font, buffer); 439 440 hb_ot_layout_substitute_finish (c->font, buffer); 441 442 return; 443 } 444 445 static inline void 446 hb_ot_substitute (hb_ot_shape_context_t *c) 447 { 448 hb_ot_substitute_default (c); 449 hb_ot_substitute_complex (c); 450 } 451 452 /* Position */ 453 454 static inline void 455 zero_mark_widths_by_unicode (hb_buffer_t *buffer) 456 { 457 unsigned int count = buffer->len; 458 for (unsigned int i = 0; i < count; i++) 459 if (_hb_glyph_info_get_general_category (&buffer->info[i]) == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) 460 { 461 buffer->pos[i].x_advance = 0; 462 buffer->pos[i].y_advance = 0; 463 } 464 } 465 466 static inline void 467 zero_mark_widths_by_gdef (hb_buffer_t *buffer) 468 { 469 unsigned int count = buffer->len; 470 for (unsigned int i = 0; i < count; i++) 471 if (_hb_glyph_info_is_mark (&buffer->info[i])) 472 { 473 buffer->pos[i].x_advance = 0; 474 buffer->pos[i].y_advance = 0; 475 } 476 } 477 478 static inline void 479 hb_ot_position_default (hb_ot_shape_context_t *c) 480 { 481 hb_direction_t direction = c->buffer->props.direction; 482 unsigned int count = c->buffer->len; 483 hb_glyph_info_t *info = c->buffer->info; 484 hb_glyph_position_t *pos = c->buffer->pos; 485 for (unsigned int i = 0; i < count; i++) 486 { 487 c->font->get_glyph_advance_for_direction (info[i].codepoint, 488 direction, 489 &pos[i].x_advance, 490 &pos[i].y_advance); 491 c->font->subtract_glyph_origin_for_direction (info[i].codepoint, 492 direction, 493 &pos[i].x_offset, 494 &pos[i].y_offset); 495 496 } 497 } 498 499 static inline bool 500 hb_ot_position_complex (hb_ot_shape_context_t *c) 501 { 502 bool ret = false; 503 unsigned int count = c->buffer->len; 504 505 switch (c->plan->shaper->zero_width_marks) 506 { 507 case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY: 508 zero_mark_widths_by_gdef (c->buffer); 509 break; 510 511 /* Not currently used for any shaper: 512 case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_UNICODE_EARLY: 513 zero_mark_widths_by_unicode (c->buffer); 514 break; 515 */ 516 517 default: 518 case HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE: 519 case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_UNICODE_LATE: 520 case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE: 521 break; 522 } 523 524 if (hb_ot_layout_has_positioning (c->face)) 525 { 526 hb_glyph_info_t *info = c->buffer->info; 527 hb_glyph_position_t *pos = c->buffer->pos; 528 529 /* Change glyph origin to what GPOS expects, apply GPOS, change it back. */ 530 531 for (unsigned int i = 0; i < count; i++) { 532 c->font->add_glyph_origin_for_direction (info[i].codepoint, 533 HB_DIRECTION_LTR, 534 &pos[i].x_offset, 535 &pos[i].y_offset); 536 } 537 538 c->plan->position (c->font, c->buffer); 539 540 for (unsigned int i = 0; i < count; i++) { 541 c->font->subtract_glyph_origin_for_direction (info[i].codepoint, 542 HB_DIRECTION_LTR, 543 &pos[i].x_offset, 544 &pos[i].y_offset); 545 } 546 547 ret = true; 548 } 549 550 switch (c->plan->shaper->zero_width_marks) 551 { 552 case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_UNICODE_LATE: 553 zero_mark_widths_by_unicode (c->buffer); 554 break; 555 556 case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE: 557 zero_mark_widths_by_gdef (c->buffer); 558 break; 559 560 default: 561 case HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE: 562 //case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_UNICODE_EARLY: 563 case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY: 564 break; 565 } 566 567 return ret; 568 } 569 570 static inline void 571 hb_ot_position (hb_ot_shape_context_t *c) 572 { 573 hb_ot_layout_position_start (c->font, c->buffer); 574 575 hb_ot_position_default (c); 576 577 hb_bool_t fallback = !hb_ot_position_complex (c); 578 579 hb_ot_layout_position_finish (c->font, c->buffer); 580 581 if (fallback && c->plan->shaper->fallback_position) 582 _hb_ot_shape_fallback_position (c->plan, c->font, c->buffer); 583 584 if (HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction)) 585 hb_buffer_reverse (c->buffer); 586 587 /* Visual fallback goes here. */ 588 589 if (fallback) 590 _hb_ot_shape_fallback_kern (c->plan, c->font, c->buffer); 591 } 592 593 594 /* Post-process */ 595 596 static void 597 hb_ot_hide_default_ignorables (hb_ot_shape_context_t *c) 598 { 599 if (c->buffer->flags & HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES) 600 return; 601 602 hb_codepoint_t space; 603 enum { 604 SPACE_DONT_KNOW, 605 SPACE_AVAILABLE, 606 SPACE_UNAVAILABLE 607 } space_status = SPACE_DONT_KNOW; 608 609 unsigned int count = c->buffer->len; 610 hb_glyph_info_t *info = c->buffer->info; 611 hb_glyph_position_t *pos = c->buffer->pos; 612 unsigned int j = 0; 613 for (unsigned int i = 0; i < count; i++) 614 { 615 if (unlikely (!_hb_glyph_info_ligated (&info[i]) && 616 _hb_glyph_info_is_default_ignorable (&info[i]))) 617 { 618 if (space_status == SPACE_DONT_KNOW) 619 space_status = c->font->get_glyph (' ', 0, &space) ? SPACE_AVAILABLE : SPACE_UNAVAILABLE; 620 621 if (space_status == SPACE_AVAILABLE) 622 { 623 info[i].codepoint = space; 624 pos[i].x_advance = 0; 625 pos[i].y_advance = 0; 626 } 627 else 628 continue; /* Delete it. */ 629 } 630 if (j != i) 631 { 632 info[j] = info[i]; 633 pos[j] = pos[i]; 634 } 635 j++; 636 } 637 c->buffer->len = j; 638 } 639 640 641 /* Pull it all together! */ 642 643 static void 644 hb_ot_shape_internal (hb_ot_shape_context_t *c) 645 { 646 c->buffer->deallocate_var_all (); 647 648 /* Save the original direction, we use it later. */ 649 c->target_direction = c->buffer->props.direction; 650 651 _hb_buffer_allocate_unicode_vars (c->buffer); 652 653 c->buffer->clear_output (); 654 655 hb_set_unicode_props (c->buffer); 656 hb_insert_dotted_circle (c->buffer, c->font); 657 hb_form_clusters (c->buffer); 658 659 hb_ensure_native_direction (c->buffer); 660 661 hb_ot_substitute (c); 662 hb_ot_position (c); 663 664 hb_ot_hide_default_ignorables (c); 665 666 _hb_buffer_deallocate_unicode_vars (c->buffer); 667 668 c->buffer->props.direction = c->target_direction; 669 670 c->buffer->deallocate_var_all (); 671 } 672 673 674 hb_bool_t 675 _hb_ot_shape (hb_shape_plan_t *shape_plan, 676 hb_font_t *font, 677 hb_buffer_t *buffer, 678 const hb_feature_t *features, 679 unsigned int num_features) 680 { 681 hb_ot_shape_context_t c = {HB_SHAPER_DATA_GET (shape_plan), font, font->face, buffer, features, num_features}; 682 hb_ot_shape_internal (&c); 683 684 return true; 685 } 686 687 688 void 689 hb_ot_shape_plan_collect_lookups (hb_shape_plan_t *shape_plan, 690 hb_tag_t table_tag, 691 hb_set_t *lookup_indexes /* OUT */) 692 { 693 /* XXX Does the first part always succeed? */ 694 HB_SHAPER_DATA_GET (shape_plan)->collect_lookups (table_tag, lookup_indexes); 695 } 696 697 698 /* TODO Move this to hb-ot-shape-normalize, make it do decompose, and make it public. */ 699 static void 700 add_char (hb_font_t *font, 701 hb_unicode_funcs_t *unicode, 702 hb_bool_t mirror, 703 hb_codepoint_t u, 704 hb_set_t *glyphs) 705 { 706 hb_codepoint_t glyph; 707 if (font->get_glyph (u, 0, &glyph)) 708 glyphs->add (glyph); 709 if (mirror) 710 { 711 hb_codepoint_t m = unicode->mirroring (u); 712 if (m != u && font->get_glyph (m, 0, &glyph)) 713 glyphs->add (glyph); 714 } 715 } 716 717 718 void 719 hb_ot_shape_glyphs_closure (hb_font_t *font, 720 hb_buffer_t *buffer, 721 const hb_feature_t *features, 722 unsigned int num_features, 723 hb_set_t *glyphs) 724 { 725 hb_ot_shape_plan_t plan; 726 727 const char *shapers[] = {"ot", NULL}; 728 hb_shape_plan_t *shape_plan = hb_shape_plan_create_cached (font->face, &buffer->props, 729 features, num_features, shapers); 730 731 bool mirror = hb_script_get_horizontal_direction (buffer->props.script) == HB_DIRECTION_RTL; 732 733 unsigned int count = buffer->len; 734 for (unsigned int i = 0; i < count; i++) 735 add_char (font, buffer->unicode, mirror, buffer->info[i].codepoint, glyphs); 736 737 hb_set_t lookups; 738 lookups.init (); 739 hb_ot_shape_plan_collect_lookups (shape_plan, HB_OT_TAG_GSUB, &lookups); 740 741 /* And find transitive closure. */ 742 hb_set_t copy; 743 copy.init (); 744 do { 745 copy.set (glyphs); 746 for (hb_codepoint_t lookup_index = -1; hb_set_next (&lookups, &lookup_index);) 747 hb_ot_layout_lookup_substitute_closure (font->face, lookup_index, glyphs); 748 } while (!copy.is_equal (glyphs)); 749 750 hb_shape_plan_destroy (shape_plan); 751 } 752