hb-ot-shape.cc revision 5497a8a274a7066c0230c850baadef681785c8bb
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 43static 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 53static 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 61static hb_tag_t vertical_features[] = { 62 HB_TAG('v','e','r','t'), 63}; 64 65 66 67static void 68hb_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 128hb_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 134void 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 145struct hb_ot_shaper_font_data_t {}; 146 147hb_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 153void 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 163hb_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 189void 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 205struct 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 225static void 226hb_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 233static void 234hb_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 hb_codepoint_t dottedcircle_glyph; 242 if (!font->get_glyph (0x25CC, 0, &dottedcircle_glyph)) 243 return; 244 245 hb_glyph_info_t dottedcircle; 246 dottedcircle.codepoint = 0x25CC; 247 _hb_glyph_info_set_unicode_props (&dottedcircle, buffer->unicode); 248 249 buffer->clear_output (); 250 251 buffer->idx = 0; 252 hb_glyph_info_t info = dottedcircle; 253 info.cluster = buffer->cur().cluster; 254 info.mask = buffer->cur().mask; 255 buffer->output_info (info); 256 while (buffer->idx < buffer->len) 257 buffer->next_glyph (); 258 259 buffer->swap_buffers (); 260} 261 262static void 263hb_form_clusters (hb_buffer_t *buffer) 264{ 265 unsigned int count = buffer->len; 266 for (unsigned int i = 1; i < count; i++) 267 if (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&buffer->info[i]))) 268 buffer->merge_clusters (i - 1, i + 1); 269} 270 271static void 272hb_ensure_native_direction (hb_buffer_t *buffer) 273{ 274 hb_direction_t direction = buffer->props.direction; 275 276 /* TODO vertical: 277 * The only BTT vertical script is Ogham, but it's not clear to me whether OpenType 278 * Ogham fonts are supposed to be implemented BTT or not. Need to research that 279 * first. */ 280 if ((HB_DIRECTION_IS_HORIZONTAL (direction) && direction != hb_script_get_horizontal_direction (buffer->props.script)) || 281 (HB_DIRECTION_IS_VERTICAL (direction) && direction != HB_DIRECTION_TTB)) 282 { 283 hb_buffer_reverse_clusters (buffer); 284 buffer->props.direction = HB_DIRECTION_REVERSE (buffer->props.direction); 285 } 286} 287 288 289/* Substitute */ 290 291static inline void 292hb_ot_mirror_chars (hb_ot_shape_context_t *c) 293{ 294 if (HB_DIRECTION_IS_FORWARD (c->target_direction)) 295 return; 296 297 hb_buffer_t *buffer = c->buffer; 298 hb_unicode_funcs_t *unicode = buffer->unicode; 299 hb_mask_t rtlm_mask = c->plan->rtlm_mask; 300 301 unsigned int count = buffer->len; 302 hb_glyph_info_t *info = buffer->info; 303 for (unsigned int i = 0; i < count; i++) { 304 hb_codepoint_t codepoint = unicode->mirroring (info[i].codepoint); 305 if (likely (codepoint == info[i].codepoint)) 306 info[i].mask |= rtlm_mask; 307 else 308 info[i].codepoint = codepoint; 309 } 310} 311 312static inline void 313hb_ot_shape_setup_masks_fraction (hb_ot_shape_context_t *c) 314{ 315 if (!c->plan->has_frac) 316 return; 317 318 hb_buffer_t *buffer = c->buffer; 319 320 /* TODO look in pre/post context text also. */ 321 unsigned int count = buffer->len; 322 hb_glyph_info_t *info = buffer->info; 323 for (unsigned int i = 0; i < count; i++) 324 { 325 if (info[i].codepoint == 0x2044) /* FRACTION SLASH */ 326 { 327 unsigned int start = i, end = i + 1; 328 while (start && 329 _hb_glyph_info_get_general_category (&info[start - 1]) == 330 HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER) 331 start--; 332 while (end < count && 333 _hb_glyph_info_get_general_category (&info[end]) == 334 HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER) 335 end++; 336 337 for (unsigned int j = start; j < i; j++) 338 info[j].mask |= c->plan->numr_mask | c->plan->frac_mask; 339 info[i].mask |= c->plan->frac_mask; 340 for (unsigned int j = i + 1; j < end; j++) 341 info[j].mask |= c->plan->frac_mask | c->plan->dnom_mask; 342 343 i = end - 1; 344 } 345 } 346} 347 348static inline void 349hb_ot_shape_initialize_masks (hb_ot_shape_context_t *c) 350{ 351 hb_ot_map_t *map = &c->plan->map; 352 hb_buffer_t *buffer = c->buffer; 353 354 hb_mask_t global_mask = map->get_global_mask (); 355 buffer->reset_masks (global_mask); 356} 357 358static inline void 359hb_ot_shape_setup_masks (hb_ot_shape_context_t *c) 360{ 361 hb_ot_map_t *map = &c->plan->map; 362 hb_buffer_t *buffer = c->buffer; 363 364 hb_ot_shape_setup_masks_fraction (c); 365 366 if (c->plan->shaper->setup_masks) 367 c->plan->shaper->setup_masks (c->plan, buffer, c->font); 368 369 for (unsigned int i = 0; i < c->num_user_features; i++) 370 { 371 const hb_feature_t *feature = &c->user_features[i]; 372 if (!(feature->start == 0 && feature->end == (unsigned int)-1)) { 373 unsigned int shift; 374 hb_mask_t mask = map->get_mask (feature->tag, &shift); 375 buffer->set_masks (feature->value << shift, mask, feature->start, feature->end); 376 } 377 } 378} 379 380static inline void 381hb_ot_map_glyphs_fast (hb_buffer_t *buffer) 382{ 383 /* Normalization process sets up glyph_index(), we just copy it. */ 384 unsigned int count = buffer->len; 385 for (unsigned int i = 0; i < count; i++) 386 buffer->info[i].codepoint = buffer->info[i].glyph_index(); 387} 388 389static inline void 390hb_synthesize_glyph_classes (hb_ot_shape_context_t *c) 391{ 392 unsigned int count = c->buffer->len; 393 hb_glyph_info_t *info = c->buffer->info; 394 for (unsigned int i = 0; i < count; i++) 395 _hb_glyph_info_set_glyph_props (&info[i], 396 _hb_glyph_info_get_general_category (&info[i]) 397 == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK ? 398 HB_OT_LAYOUT_GLYPH_PROPS_MARK : 399 HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH); 400} 401 402static inline void 403hb_ot_substitute_default (hb_ot_shape_context_t *c) 404{ 405 hb_buffer_t *buffer = c->buffer; 406 407 if (c->plan->shaper->preprocess_text) 408 c->plan->shaper->preprocess_text (c->plan, buffer, c->font); 409 410 hb_ot_shape_initialize_masks (c); 411 412 hb_ot_mirror_chars (c); 413 414 HB_BUFFER_ALLOCATE_VAR (buffer, glyph_index); 415 416 _hb_ot_shape_normalize (c->plan, buffer, c->font); 417 418 hb_ot_shape_setup_masks (c); 419 420 /* This is unfortunate to go here, but necessary... */ 421 if (!hb_ot_layout_has_positioning (c->face)) 422 _hb_ot_shape_fallback_position_recategorize_marks (c->plan, c->font, buffer); 423 424 hb_ot_map_glyphs_fast (buffer); 425 426 HB_BUFFER_DEALLOCATE_VAR (buffer, glyph_index); 427} 428 429static inline void 430hb_ot_substitute_complex (hb_ot_shape_context_t *c) 431{ 432 hb_buffer_t *buffer = c->buffer; 433 434 hb_ot_layout_substitute_start (c->font, buffer); 435 436 if (!hb_ot_layout_has_glyph_classes (c->face)) 437 hb_synthesize_glyph_classes (c); 438 439 c->plan->substitute (c->font, buffer); 440 441 hb_ot_layout_substitute_finish (c->font, buffer); 442 443 return; 444} 445 446static inline void 447hb_ot_substitute (hb_ot_shape_context_t *c) 448{ 449 hb_ot_substitute_default (c); 450 hb_ot_substitute_complex (c); 451} 452 453/* Position */ 454 455static inline void 456zero_mark_widths_by_unicode (hb_buffer_t *buffer) 457{ 458 unsigned int count = buffer->len; 459 for (unsigned int i = 0; i < count; i++) 460 if (_hb_glyph_info_get_general_category (&buffer->info[i]) == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) 461 { 462 buffer->pos[i].x_advance = 0; 463 buffer->pos[i].y_advance = 0; 464 } 465} 466 467static inline void 468zero_mark_widths_by_gdef (hb_buffer_t *buffer) 469{ 470 unsigned int count = buffer->len; 471 for (unsigned int i = 0; i < count; i++) 472 if (_hb_glyph_info_is_mark (&buffer->info[i])) 473 { 474 buffer->pos[i].x_advance = 0; 475 buffer->pos[i].y_advance = 0; 476 } 477} 478 479static inline void 480hb_ot_position_default (hb_ot_shape_context_t *c) 481{ 482 hb_direction_t direction = c->buffer->props.direction; 483 unsigned int count = c->buffer->len; 484 hb_glyph_info_t *info = c->buffer->info; 485 hb_glyph_position_t *pos = c->buffer->pos; 486 for (unsigned int i = 0; i < count; i++) 487 { 488 c->font->get_glyph_advance_for_direction (info[i].codepoint, 489 direction, 490 &pos[i].x_advance, 491 &pos[i].y_advance); 492 c->font->subtract_glyph_origin_for_direction (info[i].codepoint, 493 direction, 494 &pos[i].x_offset, 495 &pos[i].y_offset); 496 497 } 498} 499 500static inline bool 501hb_ot_position_complex (hb_ot_shape_context_t *c) 502{ 503 bool ret = false; 504 unsigned int count = c->buffer->len; 505 506 switch (c->plan->shaper->zero_width_marks) 507 { 508 case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY: 509 zero_mark_widths_by_gdef (c->buffer); 510 break; 511 512 /* Not currently used for any shaper: 513 case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_UNICODE_EARLY: 514 zero_mark_widths_by_unicode (c->buffer); 515 break; 516 */ 517 518 default: 519 case HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE: 520 case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_UNICODE_LATE: 521 case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE: 522 break; 523 } 524 525 if (hb_ot_layout_has_positioning (c->face)) 526 { 527 hb_glyph_info_t *info = c->buffer->info; 528 hb_glyph_position_t *pos = c->buffer->pos; 529 530 /* Change glyph origin to what GPOS expects, apply GPOS, change it back. */ 531 532 for (unsigned int i = 0; i < count; i++) { 533 c->font->add_glyph_origin_for_direction (info[i].codepoint, 534 HB_DIRECTION_LTR, 535 &pos[i].x_offset, 536 &pos[i].y_offset); 537 } 538 539 c->plan->position (c->font, c->buffer); 540 541 for (unsigned int i = 0; i < count; i++) { 542 c->font->subtract_glyph_origin_for_direction (info[i].codepoint, 543 HB_DIRECTION_LTR, 544 &pos[i].x_offset, 545 &pos[i].y_offset); 546 } 547 548 ret = true; 549 } 550 551 switch (c->plan->shaper->zero_width_marks) 552 { 553 case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_UNICODE_LATE: 554 zero_mark_widths_by_unicode (c->buffer); 555 break; 556 557 case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE: 558 zero_mark_widths_by_gdef (c->buffer); 559 break; 560 561 default: 562 case HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE: 563 //case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_UNICODE_EARLY: 564 case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY: 565 break; 566 } 567 568 return ret; 569} 570 571static inline void 572hb_ot_position (hb_ot_shape_context_t *c) 573{ 574 hb_ot_layout_position_start (c->font, c->buffer); 575 576 hb_ot_position_default (c); 577 578 hb_bool_t fallback = !hb_ot_position_complex (c); 579 580 hb_ot_layout_position_finish (c->font, c->buffer); 581 582 if (fallback && c->plan->shaper->fallback_position) 583 _hb_ot_shape_fallback_position (c->plan, c->font, c->buffer); 584 585 if (HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction)) 586 hb_buffer_reverse (c->buffer); 587 588 /* Visual fallback goes here. */ 589 590 if (fallback) 591 _hb_ot_shape_fallback_kern (c->plan, c->font, c->buffer); 592} 593 594 595/* Post-process */ 596 597static void 598hb_ot_hide_default_ignorables (hb_ot_shape_context_t *c) 599{ 600 if (c->buffer->flags & HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES) 601 return; 602 603 hb_codepoint_t space; 604 enum { 605 SPACE_DONT_KNOW, 606 SPACE_AVAILABLE, 607 SPACE_UNAVAILABLE 608 } space_status = SPACE_DONT_KNOW; 609 610 unsigned int count = c->buffer->len; 611 hb_glyph_info_t *info = c->buffer->info; 612 hb_glyph_position_t *pos = c->buffer->pos; 613 unsigned int j = 0; 614 for (unsigned int i = 0; i < count; i++) 615 { 616 if (unlikely (!_hb_glyph_info_ligated (&info[i]) && 617 _hb_glyph_info_is_default_ignorable (&info[i]))) 618 { 619 if (space_status == SPACE_DONT_KNOW) 620 space_status = c->font->get_glyph (' ', 0, &space) ? SPACE_AVAILABLE : SPACE_UNAVAILABLE; 621 622 if (space_status == SPACE_AVAILABLE) 623 { 624 info[i].codepoint = space; 625 pos[i].x_advance = 0; 626 pos[i].y_advance = 0; 627 } 628 else 629 continue; /* Delete it. */ 630 } 631 if (j != i) 632 { 633 info[j] = info[i]; 634 pos[j] = pos[i]; 635 } 636 j++; 637 } 638 c->buffer->len = j; 639} 640 641 642/* Pull it all together! */ 643 644static void 645hb_ot_shape_internal (hb_ot_shape_context_t *c) 646{ 647 c->buffer->deallocate_var_all (); 648 649 /* Save the original direction, we use it later. */ 650 c->target_direction = c->buffer->props.direction; 651 652 _hb_buffer_allocate_unicode_vars (c->buffer); 653 654 c->buffer->clear_output (); 655 656 hb_set_unicode_props (c->buffer); 657 hb_insert_dotted_circle (c->buffer, c->font); 658 hb_form_clusters (c->buffer); 659 660 hb_ensure_native_direction (c->buffer); 661 662 hb_ot_substitute (c); 663 hb_ot_position (c); 664 665 hb_ot_hide_default_ignorables (c); 666 667 _hb_buffer_deallocate_unicode_vars (c->buffer); 668 669 c->buffer->props.direction = c->target_direction; 670 671 c->buffer->deallocate_var_all (); 672} 673 674 675hb_bool_t 676_hb_ot_shape (hb_shape_plan_t *shape_plan, 677 hb_font_t *font, 678 hb_buffer_t *buffer, 679 const hb_feature_t *features, 680 unsigned int num_features) 681{ 682 hb_ot_shape_context_t c = {HB_SHAPER_DATA_GET (shape_plan), font, font->face, buffer, features, num_features}; 683 hb_ot_shape_internal (&c); 684 685 return true; 686} 687 688 689void 690hb_ot_shape_plan_collect_lookups (hb_shape_plan_t *shape_plan, 691 hb_tag_t table_tag, 692 hb_set_t *lookup_indexes /* OUT */) 693{ 694 /* XXX Does the first part always succeed? */ 695 HB_SHAPER_DATA_GET (shape_plan)->collect_lookups (table_tag, lookup_indexes); 696} 697 698 699/* TODO Move this to hb-ot-shape-normalize, make it do decompose, and make it public. */ 700static void 701add_char (hb_font_t *font, 702 hb_unicode_funcs_t *unicode, 703 hb_bool_t mirror, 704 hb_codepoint_t u, 705 hb_set_t *glyphs) 706{ 707 hb_codepoint_t glyph; 708 if (font->get_glyph (u, 0, &glyph)) 709 glyphs->add (glyph); 710 if (mirror) 711 { 712 hb_codepoint_t m = unicode->mirroring (u); 713 if (m != u && font->get_glyph (m, 0, &glyph)) 714 glyphs->add (glyph); 715 } 716} 717 718 719void 720hb_ot_shape_glyphs_closure (hb_font_t *font, 721 hb_buffer_t *buffer, 722 const hb_feature_t *features, 723 unsigned int num_features, 724 hb_set_t *glyphs) 725{ 726 hb_ot_shape_plan_t plan; 727 728 const char *shapers[] = {"ot", NULL}; 729 hb_shape_plan_t *shape_plan = hb_shape_plan_create_cached (font->face, &buffer->props, 730 features, num_features, shapers); 731 732 bool mirror = hb_script_get_horizontal_direction (buffer->props.script) == HB_DIRECTION_RTL; 733 734 unsigned int count = buffer->len; 735 for (unsigned int i = 0; i < count; i++) 736 add_char (font, buffer->unicode, mirror, buffer->info[i].codepoint, glyphs); 737 738 hb_set_t lookups; 739 lookups.init (); 740 hb_ot_shape_plan_collect_lookups (shape_plan, HB_OT_TAG_GSUB, &lookups); 741 742 /* And find transitive closure. */ 743 hb_set_t copy; 744 copy.init (); 745 do { 746 copy.set (glyphs); 747 for (hb_codepoint_t lookup_index = -1; hb_set_next (&lookups, &lookup_index);) 748 hb_ot_layout_lookup_substitute_closure (font->face, lookup_index, glyphs); 749 } while (!copy.is_equal (glyphs)); 750 751 hb_shape_plan_destroy (shape_plan); 752} 753