1 /* 2 * Copyright © 1998-2004 David Turner and Werner Lemberg 3 * Copyright © 2006 Behdad Esfahbod 4 * Copyright © 2007,2008,2009 Red Hat, Inc. 5 * Copyright © 2012,2013 Google, Inc. 6 * 7 * This is part of HarfBuzz, a text shaping library. 8 * 9 * Permission is hereby granted, without written agreement and without 10 * license or royalty fees, to use, copy, modify, and distribute this 11 * software and its documentation for any purpose, provided that the 12 * above copyright notice and the following two paragraphs appear in 13 * all copies of this software. 14 * 15 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR 16 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 17 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN 18 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 19 * DAMAGE. 20 * 21 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, 22 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 23 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS 24 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO 25 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 26 * 27 * Red Hat Author(s): Behdad Esfahbod 28 * Google Author(s): Behdad Esfahbod 29 */ 30 31 #include "hb-open-type-private.hh" 32 #include "hb-ot-layout-private.hh" 33 34 #include "hb-ot-layout-gdef-table.hh" 35 #include "hb-ot-layout-gsub-table.hh" 36 #include "hb-ot-layout-gpos-table.hh" 37 #include "hb-ot-layout-jstf-table.hh" 38 39 #include "hb-ot-map-private.hh" 40 41 #include <stdlib.h> 42 #include <string.h> 43 44 45 HB_SHAPER_DATA_ENSURE_DECLARE(ot, face) 46 47 hb_ot_layout_t * 48 _hb_ot_layout_create (hb_face_t *face) 49 { 50 hb_ot_layout_t *layout = (hb_ot_layout_t *) calloc (1, sizeof (hb_ot_layout_t)); 51 if (unlikely (!layout)) 52 return NULL; 53 54 layout->gdef_blob = OT::Sanitizer<OT::GDEF>::sanitize (face->reference_table (HB_OT_TAG_GDEF)); 55 layout->gdef = OT::Sanitizer<OT::GDEF>::lock_instance (layout->gdef_blob); 56 57 layout->gsub_blob = OT::Sanitizer<OT::GSUB>::sanitize (face->reference_table (HB_OT_TAG_GSUB)); 58 layout->gsub = OT::Sanitizer<OT::GSUB>::lock_instance (layout->gsub_blob); 59 60 layout->gpos_blob = OT::Sanitizer<OT::GPOS>::sanitize (face->reference_table (HB_OT_TAG_GPOS)); 61 layout->gpos = OT::Sanitizer<OT::GPOS>::lock_instance (layout->gpos_blob); 62 63 layout->gsub_lookup_count = layout->gsub->get_lookup_count (); 64 layout->gpos_lookup_count = layout->gpos->get_lookup_count (); 65 66 layout->gsub_accels = (hb_ot_layout_lookup_accelerator_t *) calloc (layout->gsub->get_lookup_count (), sizeof (hb_ot_layout_lookup_accelerator_t)); 67 layout->gpos_accels = (hb_ot_layout_lookup_accelerator_t *) calloc (layout->gpos->get_lookup_count (), sizeof (hb_ot_layout_lookup_accelerator_t)); 68 69 if (unlikely ((layout->gsub_lookup_count && !layout->gsub_accels) || 70 (layout->gpos_lookup_count && !layout->gpos_accels))) 71 { 72 _hb_ot_layout_destroy (layout); 73 return NULL; 74 } 75 76 for (unsigned int i = 0; i < layout->gsub_lookup_count; i++) 77 layout->gsub_accels[i].init (layout->gsub->get_lookup (i)); 78 for (unsigned int i = 0; i < layout->gpos_lookup_count; i++) 79 layout->gpos_accels[i].init (layout->gpos->get_lookup (i)); 80 81 return layout; 82 } 83 84 void 85 _hb_ot_layout_destroy (hb_ot_layout_t *layout) 86 { 87 for (unsigned int i = 0; i < layout->gsub_lookup_count; i++) 88 layout->gsub_accels[i].fini (); 89 for (unsigned int i = 0; i < layout->gpos_lookup_count; i++) 90 layout->gpos_accels[i].fini (); 91 92 free (layout->gsub_accels); 93 free (layout->gpos_accels); 94 95 hb_blob_destroy (layout->gdef_blob); 96 hb_blob_destroy (layout->gsub_blob); 97 hb_blob_destroy (layout->gpos_blob); 98 99 free (layout); 100 } 101 102 static inline const OT::GDEF& 103 _get_gdef (hb_face_t *face) 104 { 105 if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GDEF); 106 return *hb_ot_layout_from_face (face)->gdef; 107 } 108 static inline const OT::GSUB& 109 _get_gsub (hb_face_t *face) 110 { 111 if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GSUB); 112 return *hb_ot_layout_from_face (face)->gsub; 113 } 114 static inline const OT::GPOS& 115 _get_gpos (hb_face_t *face) 116 { 117 if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GPOS); 118 return *hb_ot_layout_from_face (face)->gpos; 119 } 120 121 122 /* 123 * GDEF 124 */ 125 126 hb_bool_t 127 hb_ot_layout_has_glyph_classes (hb_face_t *face) 128 { 129 return _get_gdef (face).has_glyph_classes (); 130 } 131 132 /** 133 * Since: 0.9.7 134 **/ 135 hb_ot_layout_glyph_class_t 136 hb_ot_layout_get_glyph_class (hb_face_t *face, 137 hb_codepoint_t glyph) 138 { 139 return (hb_ot_layout_glyph_class_t) _get_gdef (face).get_glyph_class (glyph); 140 } 141 142 /** 143 * Since: 0.9.7 144 **/ 145 void 146 hb_ot_layout_get_glyphs_in_class (hb_face_t *face, 147 hb_ot_layout_glyph_class_t klass, 148 hb_set_t *glyphs /* OUT */) 149 { 150 return _get_gdef (face).get_glyphs_in_class (klass, glyphs); 151 } 152 153 unsigned int 154 hb_ot_layout_get_attach_points (hb_face_t *face, 155 hb_codepoint_t glyph, 156 unsigned int start_offset, 157 unsigned int *point_count /* IN/OUT */, 158 unsigned int *point_array /* OUT */) 159 { 160 return _get_gdef (face).get_attach_points (glyph, start_offset, point_count, point_array); 161 } 162 163 unsigned int 164 hb_ot_layout_get_ligature_carets (hb_font_t *font, 165 hb_direction_t direction, 166 hb_codepoint_t glyph, 167 unsigned int start_offset, 168 unsigned int *caret_count /* IN/OUT */, 169 int *caret_array /* OUT */) 170 { 171 return _get_gdef (font->face).get_lig_carets (font, direction, glyph, start_offset, caret_count, caret_array); 172 } 173 174 175 /* 176 * GSUB/GPOS 177 */ 178 179 static const OT::GSUBGPOS& 180 get_gsubgpos_table (hb_face_t *face, 181 hb_tag_t table_tag) 182 { 183 switch (table_tag) { 184 case HB_OT_TAG_GSUB: return _get_gsub (face); 185 case HB_OT_TAG_GPOS: return _get_gpos (face); 186 default: return OT::Null(OT::GSUBGPOS); 187 } 188 } 189 190 191 unsigned int 192 hb_ot_layout_table_get_script_tags (hb_face_t *face, 193 hb_tag_t table_tag, 194 unsigned int start_offset, 195 unsigned int *script_count /* IN/OUT */, 196 hb_tag_t *script_tags /* OUT */) 197 { 198 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 199 200 return g.get_script_tags (start_offset, script_count, script_tags); 201 } 202 203 #define HB_OT_TAG_LATIN_SCRIPT HB_TAG ('l', 'a', 't', 'n') 204 205 hb_bool_t 206 hb_ot_layout_table_find_script (hb_face_t *face, 207 hb_tag_t table_tag, 208 hb_tag_t script_tag, 209 unsigned int *script_index) 210 { 211 ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX); 212 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 213 214 if (g.find_script_index (script_tag, script_index)) 215 return true; 216 217 /* try finding 'DFLT' */ 218 if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index)) 219 return false; 220 221 /* try with 'dflt'; MS site has had typos and many fonts use it now :(. 222 * including many versions of DejaVu Sans Mono! */ 223 if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index)) 224 return false; 225 226 /* try with 'latn'; some old fonts put their features there even though 227 they're really trying to support Thai, for example :( */ 228 if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, script_index)) 229 return false; 230 231 if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX; 232 return false; 233 } 234 235 hb_bool_t 236 hb_ot_layout_table_choose_script (hb_face_t *face, 237 hb_tag_t table_tag, 238 const hb_tag_t *script_tags, 239 unsigned int *script_index, 240 hb_tag_t *chosen_script) 241 { 242 ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX); 243 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 244 245 while (*script_tags) 246 { 247 if (g.find_script_index (*script_tags, script_index)) { 248 if (chosen_script) 249 *chosen_script = *script_tags; 250 return true; 251 } 252 script_tags++; 253 } 254 255 /* try finding 'DFLT' */ 256 if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index)) { 257 if (chosen_script) 258 *chosen_script = HB_OT_TAG_DEFAULT_SCRIPT; 259 return false; 260 } 261 262 /* try with 'dflt'; MS site has had typos and many fonts use it now :( */ 263 if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index)) { 264 if (chosen_script) 265 *chosen_script = HB_OT_TAG_DEFAULT_LANGUAGE; 266 return false; 267 } 268 269 /* try with 'latn'; some old fonts put their features there even though 270 they're really trying to support Thai, for example :( */ 271 if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, script_index)) { 272 if (chosen_script) 273 *chosen_script = HB_OT_TAG_LATIN_SCRIPT; 274 return false; 275 } 276 277 if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX; 278 if (chosen_script) 279 *chosen_script = HB_OT_LAYOUT_NO_SCRIPT_INDEX; 280 return false; 281 } 282 283 unsigned int 284 hb_ot_layout_table_get_feature_tags (hb_face_t *face, 285 hb_tag_t table_tag, 286 unsigned int start_offset, 287 unsigned int *feature_count /* IN/OUT */, 288 hb_tag_t *feature_tags /* OUT */) 289 { 290 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 291 292 return g.get_feature_tags (start_offset, feature_count, feature_tags); 293 } 294 295 hb_bool_t 296 hb_ot_layout_table_find_feature (hb_face_t *face, 297 hb_tag_t table_tag, 298 hb_tag_t feature_tag, 299 unsigned int *feature_index) 300 { 301 ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX); 302 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 303 304 unsigned int num_features = g.get_feature_count (); 305 for (unsigned int i = 0; i < num_features; i++) 306 { 307 if (feature_tag == g.get_feature_tag (i)) { 308 if (feature_index) *feature_index = i; 309 return true; 310 } 311 } 312 313 if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX; 314 return false; 315 } 316 317 318 unsigned int 319 hb_ot_layout_script_get_language_tags (hb_face_t *face, 320 hb_tag_t table_tag, 321 unsigned int script_index, 322 unsigned int start_offset, 323 unsigned int *language_count /* IN/OUT */, 324 hb_tag_t *language_tags /* OUT */) 325 { 326 const OT::Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index); 327 328 return s.get_lang_sys_tags (start_offset, language_count, language_tags); 329 } 330 331 hb_bool_t 332 hb_ot_layout_script_find_language (hb_face_t *face, 333 hb_tag_t table_tag, 334 unsigned int script_index, 335 hb_tag_t language_tag, 336 unsigned int *language_index) 337 { 338 ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX); 339 const OT::Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index); 340 341 if (s.find_lang_sys_index (language_tag, language_index)) 342 return true; 343 344 /* try with 'dflt'; MS site has had typos and many fonts use it now :( */ 345 if (s.find_lang_sys_index (HB_OT_TAG_DEFAULT_LANGUAGE, language_index)) 346 return false; 347 348 if (language_index) *language_index = HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX; 349 return false; 350 } 351 352 hb_bool_t 353 hb_ot_layout_language_get_required_feature_index (hb_face_t *face, 354 hb_tag_t table_tag, 355 unsigned int script_index, 356 unsigned int language_index, 357 unsigned int *feature_index) 358 { 359 return hb_ot_layout_language_get_required_feature (face, 360 table_tag, 361 script_index, 362 language_index, 363 feature_index, 364 NULL); 365 } 366 367 /** 368 * Since: 0.9.30 369 **/ 370 hb_bool_t 371 hb_ot_layout_language_get_required_feature (hb_face_t *face, 372 hb_tag_t table_tag, 373 unsigned int script_index, 374 unsigned int language_index, 375 unsigned int *feature_index, 376 hb_tag_t *feature_tag) 377 { 378 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 379 const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index); 380 381 unsigned int index = l.get_required_feature_index (); 382 if (feature_index) *feature_index = index; 383 if (feature_tag) *feature_tag = g.get_feature_tag (index); 384 385 return l.has_required_feature (); 386 } 387 388 unsigned int 389 hb_ot_layout_language_get_feature_indexes (hb_face_t *face, 390 hb_tag_t table_tag, 391 unsigned int script_index, 392 unsigned int language_index, 393 unsigned int start_offset, 394 unsigned int *feature_count /* IN/OUT */, 395 unsigned int *feature_indexes /* OUT */) 396 { 397 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 398 const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index); 399 400 return l.get_feature_indexes (start_offset, feature_count, feature_indexes); 401 } 402 403 unsigned int 404 hb_ot_layout_language_get_feature_tags (hb_face_t *face, 405 hb_tag_t table_tag, 406 unsigned int script_index, 407 unsigned int language_index, 408 unsigned int start_offset, 409 unsigned int *feature_count /* IN/OUT */, 410 hb_tag_t *feature_tags /* OUT */) 411 { 412 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 413 const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index); 414 415 ASSERT_STATIC (sizeof (unsigned int) == sizeof (hb_tag_t)); 416 unsigned int ret = l.get_feature_indexes (start_offset, feature_count, (unsigned int *) feature_tags); 417 418 if (feature_tags) { 419 unsigned int count = *feature_count; 420 for (unsigned int i = 0; i < count; i++) 421 feature_tags[i] = g.get_feature_tag ((unsigned int) feature_tags[i]); 422 } 423 424 return ret; 425 } 426 427 428 hb_bool_t 429 hb_ot_layout_language_find_feature (hb_face_t *face, 430 hb_tag_t table_tag, 431 unsigned int script_index, 432 unsigned int language_index, 433 hb_tag_t feature_tag, 434 unsigned int *feature_index) 435 { 436 ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX); 437 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 438 const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index); 439 440 unsigned int num_features = l.get_feature_count (); 441 for (unsigned int i = 0; i < num_features; i++) { 442 unsigned int f_index = l.get_feature_index (i); 443 444 if (feature_tag == g.get_feature_tag (f_index)) { 445 if (feature_index) *feature_index = f_index; 446 return true; 447 } 448 } 449 450 if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX; 451 return false; 452 } 453 454 /** 455 * Since: 0.9.7 456 **/ 457 unsigned int 458 hb_ot_layout_feature_get_lookups (hb_face_t *face, 459 hb_tag_t table_tag, 460 unsigned int feature_index, 461 unsigned int start_offset, 462 unsigned int *lookup_count /* IN/OUT */, 463 unsigned int *lookup_indexes /* OUT */) 464 { 465 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 466 const OT::Feature &f = g.get_feature (feature_index); 467 468 return f.get_lookup_indexes (start_offset, lookup_count, lookup_indexes); 469 } 470 471 /** 472 * Since: 0.9.22 473 **/ 474 unsigned int 475 hb_ot_layout_table_get_lookup_count (hb_face_t *face, 476 hb_tag_t table_tag) 477 { 478 switch (table_tag) 479 { 480 case HB_OT_TAG_GSUB: 481 { 482 return hb_ot_layout_from_face (face)->gsub_lookup_count; 483 } 484 case HB_OT_TAG_GPOS: 485 { 486 return hb_ot_layout_from_face (face)->gpos_lookup_count; 487 } 488 } 489 return 0; 490 } 491 492 static void 493 _hb_ot_layout_collect_lookups_lookups (hb_face_t *face, 494 hb_tag_t table_tag, 495 unsigned int feature_index, 496 hb_set_t *lookup_indexes /* OUT */) 497 { 498 unsigned int lookup_indices[32]; 499 unsigned int offset, len; 500 501 offset = 0; 502 do { 503 len = ARRAY_LENGTH (lookup_indices); 504 hb_ot_layout_feature_get_lookups (face, 505 table_tag, 506 feature_index, 507 offset, &len, 508 lookup_indices); 509 510 for (unsigned int i = 0; i < len; i++) 511 lookup_indexes->add (lookup_indices[i]); 512 513 offset += len; 514 } while (len == ARRAY_LENGTH (lookup_indices)); 515 } 516 517 static void 518 _hb_ot_layout_collect_lookups_features (hb_face_t *face, 519 hb_tag_t table_tag, 520 unsigned int script_index, 521 unsigned int language_index, 522 const hb_tag_t *features, 523 hb_set_t *lookup_indexes /* OUT */) 524 { 525 if (!features) 526 { 527 unsigned int required_feature_index; 528 if (hb_ot_layout_language_get_required_feature (face, 529 table_tag, 530 script_index, 531 language_index, 532 &required_feature_index, 533 NULL)) 534 _hb_ot_layout_collect_lookups_lookups (face, 535 table_tag, 536 required_feature_index, 537 lookup_indexes); 538 539 /* All features */ 540 unsigned int feature_indices[32]; 541 unsigned int offset, len; 542 543 offset = 0; 544 do { 545 len = ARRAY_LENGTH (feature_indices); 546 hb_ot_layout_language_get_feature_indexes (face, 547 table_tag, 548 script_index, 549 language_index, 550 offset, &len, 551 feature_indices); 552 553 for (unsigned int i = 0; i < len; i++) 554 _hb_ot_layout_collect_lookups_lookups (face, 555 table_tag, 556 feature_indices[i], 557 lookup_indexes); 558 559 offset += len; 560 } while (len == ARRAY_LENGTH (feature_indices)); 561 } 562 else 563 { 564 for (; *features; features++) 565 { 566 unsigned int feature_index; 567 if (hb_ot_layout_language_find_feature (face, 568 table_tag, 569 script_index, 570 language_index, 571 *features, 572 &feature_index)) 573 _hb_ot_layout_collect_lookups_lookups (face, 574 table_tag, 575 feature_index, 576 lookup_indexes); 577 } 578 } 579 } 580 581 static void 582 _hb_ot_layout_collect_lookups_languages (hb_face_t *face, 583 hb_tag_t table_tag, 584 unsigned int script_index, 585 const hb_tag_t *languages, 586 const hb_tag_t *features, 587 hb_set_t *lookup_indexes /* OUT */) 588 { 589 _hb_ot_layout_collect_lookups_features (face, 590 table_tag, 591 script_index, 592 HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX, 593 features, 594 lookup_indexes); 595 596 if (!languages) 597 { 598 /* All languages */ 599 unsigned int count = hb_ot_layout_script_get_language_tags (face, 600 table_tag, 601 script_index, 602 0, NULL, NULL); 603 for (unsigned int language_index = 0; language_index < count; language_index++) 604 _hb_ot_layout_collect_lookups_features (face, 605 table_tag, 606 script_index, 607 language_index, 608 features, 609 lookup_indexes); 610 } 611 else 612 { 613 for (; *languages; languages++) 614 { 615 unsigned int language_index; 616 if (hb_ot_layout_script_find_language (face, 617 table_tag, 618 script_index, 619 *languages, 620 &language_index)) 621 _hb_ot_layout_collect_lookups_features (face, 622 table_tag, 623 script_index, 624 language_index, 625 features, 626 lookup_indexes); 627 } 628 } 629 } 630 631 /** 632 * Since: 0.9.8 633 **/ 634 void 635 hb_ot_layout_collect_lookups (hb_face_t *face, 636 hb_tag_t table_tag, 637 const hb_tag_t *scripts, 638 const hb_tag_t *languages, 639 const hb_tag_t *features, 640 hb_set_t *lookup_indexes /* OUT */) 641 { 642 if (!scripts) 643 { 644 /* All scripts */ 645 unsigned int count = hb_ot_layout_table_get_script_tags (face, 646 table_tag, 647 0, NULL, NULL); 648 for (unsigned int script_index = 0; script_index < count; script_index++) 649 _hb_ot_layout_collect_lookups_languages (face, 650 table_tag, 651 script_index, 652 languages, 653 features, 654 lookup_indexes); 655 } 656 else 657 { 658 for (; *scripts; scripts++) 659 { 660 unsigned int script_index; 661 if (hb_ot_layout_table_find_script (face, 662 table_tag, 663 *scripts, 664 &script_index)) 665 _hb_ot_layout_collect_lookups_languages (face, 666 table_tag, 667 script_index, 668 languages, 669 features, 670 lookup_indexes); 671 } 672 } 673 } 674 675 /** 676 * Since: 0.9.7 677 **/ 678 void 679 hb_ot_layout_lookup_collect_glyphs (hb_face_t *face, 680 hb_tag_t table_tag, 681 unsigned int lookup_index, 682 hb_set_t *glyphs_before, /* OUT. May be NULL */ 683 hb_set_t *glyphs_input, /* OUT. May be NULL */ 684 hb_set_t *glyphs_after, /* OUT. May be NULL */ 685 hb_set_t *glyphs_output /* OUT. May be NULL */) 686 { 687 if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return; 688 689 OT::hb_collect_glyphs_context_t c (face, 690 glyphs_before, 691 glyphs_input, 692 glyphs_after, 693 glyphs_output); 694 695 switch (table_tag) 696 { 697 case HB_OT_TAG_GSUB: 698 { 699 const OT::SubstLookup& l = hb_ot_layout_from_face (face)->gsub->get_lookup (lookup_index); 700 l.collect_glyphs (&c); 701 return; 702 } 703 case HB_OT_TAG_GPOS: 704 { 705 const OT::PosLookup& l = hb_ot_layout_from_face (face)->gpos->get_lookup (lookup_index); 706 l.collect_glyphs (&c); 707 return; 708 } 709 } 710 } 711 712 713 /* 714 * OT::GSUB 715 */ 716 717 hb_bool_t 718 hb_ot_layout_has_substitution (hb_face_t *face) 719 { 720 return &_get_gsub (face) != &OT::Null(OT::GSUB); 721 } 722 723 /** 724 * Since: 0.9.7 725 **/ 726 hb_bool_t 727 hb_ot_layout_lookup_would_substitute (hb_face_t *face, 728 unsigned int lookup_index, 729 const hb_codepoint_t *glyphs, 730 unsigned int glyphs_length, 731 hb_bool_t zero_context) 732 { 733 if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return false; 734 return hb_ot_layout_lookup_would_substitute_fast (face, lookup_index, glyphs, glyphs_length, zero_context); 735 } 736 737 hb_bool_t 738 hb_ot_layout_lookup_would_substitute_fast (hb_face_t *face, 739 unsigned int lookup_index, 740 const hb_codepoint_t *glyphs, 741 unsigned int glyphs_length, 742 hb_bool_t zero_context) 743 { 744 if (unlikely (lookup_index >= hb_ot_layout_from_face (face)->gsub_lookup_count)) return false; 745 OT::hb_would_apply_context_t c (face, glyphs, glyphs_length, zero_context); 746 747 const OT::SubstLookup& l = hb_ot_layout_from_face (face)->gsub->get_lookup (lookup_index); 748 749 return l.would_apply (&c, &hb_ot_layout_from_face (face)->gsub_accels[lookup_index]); 750 } 751 752 void 753 hb_ot_layout_substitute_start (hb_font_t *font, hb_buffer_t *buffer) 754 { 755 OT::GSUB::substitute_start (font, buffer); 756 } 757 758 void 759 hb_ot_layout_substitute_finish (hb_font_t *font, hb_buffer_t *buffer) 760 { 761 OT::GSUB::substitute_finish (font, buffer); 762 } 763 764 /** 765 * Since: 0.9.7 766 **/ 767 void 768 hb_ot_layout_lookup_substitute_closure (hb_face_t *face, 769 unsigned int lookup_index, 770 hb_set_t *glyphs) 771 { 772 OT::hb_closure_context_t c (face, glyphs); 773 774 const OT::SubstLookup& l = _get_gsub (face).get_lookup (lookup_index); 775 776 l.closure (&c); 777 } 778 779 /* 780 * OT::GPOS 781 */ 782 783 hb_bool_t 784 hb_ot_layout_has_positioning (hb_face_t *face) 785 { 786 return &_get_gpos (face) != &OT::Null(OT::GPOS); 787 } 788 789 void 790 hb_ot_layout_position_start (hb_font_t *font, hb_buffer_t *buffer) 791 { 792 OT::GPOS::position_start (font, buffer); 793 } 794 795 void 796 hb_ot_layout_position_finish (hb_font_t *font, hb_buffer_t *buffer) 797 { 798 OT::GPOS::position_finish (font, buffer); 799 } 800 801 /** 802 * Since: 0.9.10 803 **/ 804 hb_bool_t 805 hb_ot_layout_get_size_params (hb_face_t *face, 806 unsigned int *design_size, /* OUT. May be NULL */ 807 unsigned int *subfamily_id, /* OUT. May be NULL */ 808 unsigned int *subfamily_name_id, /* OUT. May be NULL */ 809 unsigned int *range_start, /* OUT. May be NULL */ 810 unsigned int *range_end /* OUT. May be NULL */) 811 { 812 const OT::GPOS &gpos = _get_gpos (face); 813 const hb_tag_t tag = HB_TAG ('s','i','z','e'); 814 815 unsigned int num_features = gpos.get_feature_count (); 816 for (unsigned int i = 0; i < num_features; i++) 817 { 818 if (tag == gpos.get_feature_tag (i)) 819 { 820 const OT::Feature &f = gpos.get_feature (i); 821 const OT::FeatureParamsSize ¶ms = f.get_feature_params ().get_size_params (tag); 822 823 if (params.designSize) 824 { 825 #define PARAM(a, A) if (a) *a = params.A 826 PARAM (design_size, designSize); 827 PARAM (subfamily_id, subfamilyID); 828 PARAM (subfamily_name_id, subfamilyNameID); 829 PARAM (range_start, rangeStart); 830 PARAM (range_end, rangeEnd); 831 #undef PARAM 832 833 return true; 834 } 835 } 836 } 837 838 #define PARAM(a, A) if (a) *a = 0 839 PARAM (design_size, designSize); 840 PARAM (subfamily_id, subfamilyID); 841 PARAM (subfamily_name_id, subfamilyNameID); 842 PARAM (range_start, rangeStart); 843 PARAM (range_end, rangeEnd); 844 #undef PARAM 845 846 return false; 847 } 848 849 850 /* 851 * Parts of different types are implemented here such that they have direct 852 * access to GSUB/GPOS lookups. 853 */ 854 855 856 struct GSUBProxy 857 { 858 static const unsigned int table_index = 0; 859 static const bool inplace = false; 860 typedef OT::SubstLookup Lookup; 861 862 GSUBProxy (hb_face_t *face) : 863 table (*hb_ot_layout_from_face (face)->gsub), 864 accels (hb_ot_layout_from_face (face)->gsub_accels) {} 865 866 const OT::GSUB &table; 867 const hb_ot_layout_lookup_accelerator_t *accels; 868 }; 869 870 struct GPOSProxy 871 { 872 static const unsigned int table_index = 1; 873 static const bool inplace = true; 874 typedef OT::PosLookup Lookup; 875 876 GPOSProxy (hb_face_t *face) : 877 table (*hb_ot_layout_from_face (face)->gpos), 878 accels (hb_ot_layout_from_face (face)->gpos_accels) {} 879 880 const OT::GPOS &table; 881 const hb_ot_layout_lookup_accelerator_t *accels; 882 }; 883 884 885 template <typename Obj> 886 static inline bool 887 apply_forward (OT::hb_apply_context_t *c, 888 const Obj &obj, 889 const hb_ot_layout_lookup_accelerator_t &accel) 890 { 891 bool ret = false; 892 hb_buffer_t *buffer = c->buffer; 893 while (buffer->idx < buffer->len) 894 { 895 if (accel.may_have (buffer->cur().codepoint) && 896 (buffer->cur().mask & c->lookup_mask) && 897 c->check_glyph_property (&buffer->cur(), c->lookup_props) && 898 obj.apply (c)) 899 ret = true; 900 else 901 buffer->next_glyph (); 902 } 903 return ret; 904 } 905 906 template <typename Obj> 907 static inline bool 908 apply_backward (OT::hb_apply_context_t *c, 909 const Obj &obj, 910 const hb_ot_layout_lookup_accelerator_t &accel) 911 { 912 bool ret = false; 913 hb_buffer_t *buffer = c->buffer; 914 do 915 { 916 if (accel.may_have (buffer->cur().codepoint) && 917 (buffer->cur().mask & c->lookup_mask) && 918 c->check_glyph_property (&buffer->cur(), c->lookup_props) && 919 obj.apply (c)) 920 ret = true; 921 /* The reverse lookup doesn't "advance" cursor (for good reason). */ 922 buffer->idx--; 923 924 } 925 while ((int) buffer->idx >= 0); 926 return ret; 927 } 928 929 struct hb_apply_forward_context_t : 930 OT::hb_dispatch_context_t<hb_apply_forward_context_t, bool, HB_DEBUG_APPLY> 931 { 932 inline const char *get_name (void) { return "APPLY_FWD"; } 933 template <typename T> 934 inline return_t dispatch (const T &obj) { return apply_forward (c, obj, accel); } 935 static return_t default_return_value (void) { return false; } 936 bool stop_sublookup_iteration (return_t r HB_UNUSED) const { return true; } 937 938 hb_apply_forward_context_t (OT::hb_apply_context_t *c_, 939 const hb_ot_layout_lookup_accelerator_t &accel_) : 940 c (c_), 941 accel (accel_), 942 debug_depth (0) {} 943 944 OT::hb_apply_context_t *c; 945 const hb_ot_layout_lookup_accelerator_t &accel; 946 unsigned int debug_depth; 947 }; 948 949 template <typename Proxy> 950 static inline void 951 apply_string (OT::hb_apply_context_t *c, 952 const typename Proxy::Lookup &lookup, 953 const hb_ot_layout_lookup_accelerator_t &accel) 954 { 955 hb_buffer_t *buffer = c->buffer; 956 957 if (unlikely (!buffer->len || !c->lookup_mask)) 958 return; 959 960 c->set_lookup_props (lookup.get_props ()); 961 962 if (likely (!lookup.is_reverse ())) 963 { 964 /* in/out forward substitution/positioning */ 965 if (Proxy::table_index == 0) 966 buffer->clear_output (); 967 buffer->idx = 0; 968 969 bool ret; 970 if (lookup.get_subtable_count () == 1) 971 { 972 hb_apply_forward_context_t c_forward (c, accel); 973 ret = lookup.dispatch (&c_forward); 974 } 975 else 976 ret = apply_forward (c, lookup, accel); 977 if (ret) 978 { 979 if (!Proxy::inplace) 980 buffer->swap_buffers (); 981 else 982 assert (!buffer->has_separate_output ()); 983 } 984 } 985 else 986 { 987 /* in-place backward substitution/positioning */ 988 if (Proxy::table_index == 0) 989 buffer->remove_output (); 990 buffer->idx = buffer->len - 1; 991 992 apply_backward (c, lookup, accel); 993 } 994 } 995 996 template <typename Proxy> 997 inline void hb_ot_map_t::apply (const Proxy &proxy, 998 const hb_ot_shape_plan_t *plan, 999 hb_font_t *font, 1000 hb_buffer_t *buffer) const 1001 { 1002 const unsigned int table_index = proxy.table_index; 1003 unsigned int i = 0; 1004 OT::hb_apply_context_t c (table_index, font, buffer); 1005 c.set_recurse_func (Proxy::Lookup::apply_recurse_func); 1006 1007 for (unsigned int stage_index = 0; stage_index < stages[table_index].len; stage_index++) { 1008 const stage_map_t *stage = &stages[table_index][stage_index]; 1009 for (; i < stage->last_lookup; i++) 1010 { 1011 #if 0 1012 char buf[4096]; 1013 hb_buffer_serialize_glyphs (buffer, 0, buffer->len, 1014 buf, sizeof (buf), NULL, 1015 font, 1016 HB_BUFFER_SERIALIZE_FORMAT_TEXT, 1017 Proxy::table_index == 0 ? 1018 HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS : 1019 HB_BUFFER_SERIALIZE_FLAG_DEFAULT); 1020 printf ("buf: [%s]\n", buf); 1021 #endif 1022 1023 unsigned int lookup_index = lookups[table_index][i].index; 1024 c.set_lookup_index (lookup_index); 1025 c.set_lookup_mask (lookups[table_index][i].mask); 1026 c.set_auto_zwj (lookups[table_index][i].auto_zwj); 1027 apply_string<Proxy> (&c, 1028 proxy.table.get_lookup (lookup_index), 1029 proxy.accels[lookup_index]); 1030 } 1031 1032 if (stage->pause_func) 1033 { 1034 buffer->clear_output (); 1035 stage->pause_func (plan, font, buffer); 1036 } 1037 } 1038 } 1039 1040 void hb_ot_map_t::substitute (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const 1041 { 1042 GSUBProxy proxy (font->face); 1043 apply (proxy, plan, font, buffer); 1044 } 1045 1046 void hb_ot_map_t::position (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const 1047 { 1048 GPOSProxy proxy (font->face); 1049 apply (proxy, plan, font, buffer); 1050 } 1051 1052 HB_INTERNAL void 1053 hb_ot_layout_substitute_lookup (OT::hb_apply_context_t *c, 1054 const OT::SubstLookup &lookup, 1055 const hb_ot_layout_lookup_accelerator_t &accel) 1056 { 1057 apply_string<GSUBProxy> (c, lookup, accel); 1058 }