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