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 (layout->gsub->get_lookup (i)); 88 for (unsigned int i = 0; i < layout->gpos_lookup_count; i++) 89 layout->gpos_accels[i].fini (layout->gpos->get_lookup (i)); 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 const OT::LangSys &l = get_gsubgpos_table (face, table_tag).get_script (script_index).get_lang_sys (language_index); 331 332 if (feature_index) *feature_index = l.get_required_feature_index (); 333 334 return l.has_required_feature (); 335 } 336 337 unsigned int 338 hb_ot_layout_language_get_feature_indexes (hb_face_t *face, 339 hb_tag_t table_tag, 340 unsigned int script_index, 341 unsigned int language_index, 342 unsigned int start_offset, 343 unsigned int *feature_count /* IN/OUT */, 344 unsigned int *feature_indexes /* OUT */) 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 return l.get_feature_indexes (start_offset, feature_count, feature_indexes); 350 } 351 352 unsigned int 353 hb_ot_layout_language_get_feature_tags (hb_face_t *face, 354 hb_tag_t table_tag, 355 unsigned int script_index, 356 unsigned int language_index, 357 unsigned int start_offset, 358 unsigned int *feature_count /* IN/OUT */, 359 hb_tag_t *feature_tags /* OUT */) 360 { 361 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 362 const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index); 363 364 ASSERT_STATIC (sizeof (unsigned int) == sizeof (hb_tag_t)); 365 unsigned int ret = l.get_feature_indexes (start_offset, feature_count, (unsigned int *) feature_tags); 366 367 if (feature_tags) { 368 unsigned int count = *feature_count; 369 for (unsigned int i = 0; i < count; i++) 370 feature_tags[i] = g.get_feature_tag ((unsigned int) feature_tags[i]); 371 } 372 373 return ret; 374 } 375 376 377 hb_bool_t 378 hb_ot_layout_language_find_feature (hb_face_t *face, 379 hb_tag_t table_tag, 380 unsigned int script_index, 381 unsigned int language_index, 382 hb_tag_t feature_tag, 383 unsigned int *feature_index) 384 { 385 ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX); 386 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 387 const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index); 388 389 unsigned int num_features = l.get_feature_count (); 390 for (unsigned int i = 0; i < num_features; i++) { 391 unsigned int f_index = l.get_feature_index (i); 392 393 if (feature_tag == g.get_feature_tag (f_index)) { 394 if (feature_index) *feature_index = f_index; 395 return true; 396 } 397 } 398 399 if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX; 400 return false; 401 } 402 403 unsigned int 404 hb_ot_layout_feature_get_lookups (hb_face_t *face, 405 hb_tag_t table_tag, 406 unsigned int feature_index, 407 unsigned int start_offset, 408 unsigned int *lookup_count /* IN/OUT */, 409 unsigned int *lookup_indexes /* OUT */) 410 { 411 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 412 const OT::Feature &f = g.get_feature (feature_index); 413 414 return f.get_lookup_indexes (start_offset, lookup_count, lookup_indexes); 415 } 416 417 unsigned int 418 hb_ot_layout_table_get_lookup_count (hb_face_t *face, 419 hb_tag_t table_tag) 420 { 421 switch (table_tag) 422 { 423 case HB_OT_TAG_GSUB: 424 { 425 return hb_ot_layout_from_face (face)->gsub_lookup_count; 426 } 427 case HB_OT_TAG_GPOS: 428 { 429 return hb_ot_layout_from_face (face)->gpos_lookup_count; 430 } 431 } 432 return 0; 433 } 434 435 static void 436 _hb_ot_layout_collect_lookups_lookups (hb_face_t *face, 437 hb_tag_t table_tag, 438 unsigned int feature_index, 439 hb_set_t *lookup_indexes /* OUT */) 440 { 441 unsigned int lookup_indices[32]; 442 unsigned int offset, len; 443 444 offset = 0; 445 do { 446 len = ARRAY_LENGTH (lookup_indices); 447 hb_ot_layout_feature_get_lookups (face, 448 table_tag, 449 feature_index, 450 offset, &len, 451 lookup_indices); 452 453 for (unsigned int i = 0; i < len; i++) 454 lookup_indexes->add (lookup_indices[i]); 455 456 offset += len; 457 } while (len == ARRAY_LENGTH (lookup_indices)); 458 } 459 460 static void 461 _hb_ot_layout_collect_lookups_features (hb_face_t *face, 462 hb_tag_t table_tag, 463 unsigned int script_index, 464 unsigned int language_index, 465 const hb_tag_t *features, 466 hb_set_t *lookup_indexes /* OUT */) 467 { 468 if (!features) 469 { 470 unsigned int required_feature_index; 471 if (hb_ot_layout_language_get_required_feature_index (face, 472 table_tag, 473 script_index, 474 language_index, 475 &required_feature_index)) 476 _hb_ot_layout_collect_lookups_lookups (face, 477 table_tag, 478 required_feature_index, 479 lookup_indexes); 480 481 /* All features */ 482 unsigned int feature_indices[32]; 483 unsigned int offset, len; 484 485 offset = 0; 486 do { 487 len = ARRAY_LENGTH (feature_indices); 488 hb_ot_layout_language_get_feature_indexes (face, 489 table_tag, 490 script_index, 491 language_index, 492 offset, &len, 493 feature_indices); 494 495 for (unsigned int i = 0; i < len; i++) 496 _hb_ot_layout_collect_lookups_lookups (face, 497 table_tag, 498 feature_indices[i], 499 lookup_indexes); 500 501 offset += len; 502 } while (len == ARRAY_LENGTH (feature_indices)); 503 } 504 else 505 { 506 for (; *features; features++) 507 { 508 unsigned int feature_index; 509 if (hb_ot_layout_language_find_feature (face, 510 table_tag, 511 script_index, 512 language_index, 513 *features, 514 &feature_index)) 515 _hb_ot_layout_collect_lookups_lookups (face, 516 table_tag, 517 feature_index, 518 lookup_indexes); 519 } 520 } 521 } 522 523 static void 524 _hb_ot_layout_collect_lookups_languages (hb_face_t *face, 525 hb_tag_t table_tag, 526 unsigned int script_index, 527 const hb_tag_t *languages, 528 const hb_tag_t *features, 529 hb_set_t *lookup_indexes /* OUT */) 530 { 531 _hb_ot_layout_collect_lookups_features (face, 532 table_tag, 533 script_index, 534 HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX, 535 features, 536 lookup_indexes); 537 538 if (!languages) 539 { 540 /* All languages */ 541 unsigned int count = hb_ot_layout_script_get_language_tags (face, 542 table_tag, 543 script_index, 544 0, NULL, NULL); 545 for (unsigned int language_index = 0; language_index < count; language_index++) 546 _hb_ot_layout_collect_lookups_features (face, 547 table_tag, 548 script_index, 549 language_index, 550 features, 551 lookup_indexes); 552 } 553 else 554 { 555 for (; *languages; languages++) 556 { 557 unsigned int language_index; 558 if (hb_ot_layout_script_find_language (face, 559 table_tag, 560 script_index, 561 *languages, 562 &language_index)) 563 _hb_ot_layout_collect_lookups_features (face, 564 table_tag, 565 script_index, 566 language_index, 567 features, 568 lookup_indexes); 569 } 570 } 571 } 572 573 void 574 hb_ot_layout_collect_lookups (hb_face_t *face, 575 hb_tag_t table_tag, 576 const hb_tag_t *scripts, 577 const hb_tag_t *languages, 578 const hb_tag_t *features, 579 hb_set_t *lookup_indexes /* OUT */) 580 { 581 if (!scripts) 582 { 583 /* All scripts */ 584 unsigned int count = hb_ot_layout_table_get_script_tags (face, 585 table_tag, 586 0, NULL, NULL); 587 for (unsigned int script_index = 0; script_index < count; script_index++) 588 _hb_ot_layout_collect_lookups_languages (face, 589 table_tag, 590 script_index, 591 languages, 592 features, 593 lookup_indexes); 594 } 595 else 596 { 597 for (; *scripts; scripts++) 598 { 599 unsigned int script_index; 600 if (hb_ot_layout_table_find_script (face, 601 table_tag, 602 *scripts, 603 &script_index)) 604 _hb_ot_layout_collect_lookups_languages (face, 605 table_tag, 606 script_index, 607 languages, 608 features, 609 lookup_indexes); 610 } 611 } 612 } 613 614 void 615 hb_ot_layout_lookup_collect_glyphs (hb_face_t *face, 616 hb_tag_t table_tag, 617 unsigned int lookup_index, 618 hb_set_t *glyphs_before, /* OUT. May be NULL */ 619 hb_set_t *glyphs_input, /* OUT. May be NULL */ 620 hb_set_t *glyphs_after, /* OUT. May be NULL */ 621 hb_set_t *glyphs_output /* OUT. May be NULL */) 622 { 623 if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return; 624 625 OT::hb_collect_glyphs_context_t c (face, 626 glyphs_before, 627 glyphs_input, 628 glyphs_after, 629 glyphs_output); 630 631 switch (table_tag) 632 { 633 case HB_OT_TAG_GSUB: 634 { 635 const OT::SubstLookup& l = hb_ot_layout_from_face (face)->gsub->get_lookup (lookup_index); 636 l.collect_glyphs (&c); 637 return; 638 } 639 case HB_OT_TAG_GPOS: 640 { 641 const OT::PosLookup& l = hb_ot_layout_from_face (face)->gpos->get_lookup (lookup_index); 642 l.collect_glyphs (&c); 643 return; 644 } 645 } 646 } 647 648 649 /* 650 * OT::GSUB 651 */ 652 653 hb_bool_t 654 hb_ot_layout_has_substitution (hb_face_t *face) 655 { 656 return &_get_gsub (face) != &OT::Null(OT::GSUB); 657 } 658 659 hb_bool_t 660 hb_ot_layout_lookup_would_substitute (hb_face_t *face, 661 unsigned int lookup_index, 662 const hb_codepoint_t *glyphs, 663 unsigned int glyphs_length, 664 hb_bool_t zero_context) 665 { 666 if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return false; 667 return hb_ot_layout_lookup_would_substitute_fast (face, lookup_index, glyphs, glyphs_length, zero_context); 668 } 669 670 hb_bool_t 671 hb_ot_layout_lookup_would_substitute_fast (hb_face_t *face, 672 unsigned int lookup_index, 673 const hb_codepoint_t *glyphs, 674 unsigned int glyphs_length, 675 hb_bool_t zero_context) 676 { 677 if (unlikely (lookup_index >= hb_ot_layout_from_face (face)->gsub_lookup_count)) return false; 678 OT::hb_would_apply_context_t c (face, glyphs, glyphs_length, zero_context); 679 680 const OT::SubstLookup& l = hb_ot_layout_from_face (face)->gsub->get_lookup (lookup_index); 681 682 return l.would_apply (&c, &hb_ot_layout_from_face (face)->gsub_accels[lookup_index].digest); 683 } 684 685 void 686 hb_ot_layout_substitute_start (hb_font_t *font, hb_buffer_t *buffer) 687 { 688 OT::GSUB::substitute_start (font, buffer); 689 } 690 691 void 692 hb_ot_layout_substitute_finish (hb_font_t *font, hb_buffer_t *buffer) 693 { 694 OT::GSUB::substitute_finish (font, buffer); 695 } 696 697 void 698 hb_ot_layout_lookup_substitute_closure (hb_face_t *face, 699 unsigned int lookup_index, 700 hb_set_t *glyphs) 701 { 702 OT::hb_closure_context_t c (face, glyphs); 703 704 const OT::SubstLookup& l = _get_gsub (face).get_lookup (lookup_index); 705 706 l.closure (&c); 707 } 708 709 /* 710 * OT::GPOS 711 */ 712 713 hb_bool_t 714 hb_ot_layout_has_positioning (hb_face_t *face) 715 { 716 return &_get_gpos (face) != &OT::Null(OT::GPOS); 717 } 718 719 void 720 hb_ot_layout_position_start (hb_font_t *font, hb_buffer_t *buffer) 721 { 722 OT::GPOS::position_start (font, buffer); 723 } 724 725 void 726 hb_ot_layout_position_finish (hb_font_t *font, hb_buffer_t *buffer) 727 { 728 OT::GPOS::position_finish (font, buffer); 729 } 730 731 hb_bool_t 732 hb_ot_layout_get_size_params (hb_face_t *face, 733 unsigned int *design_size, /* OUT. May be NULL */ 734 unsigned int *subfamily_id, /* OUT. May be NULL */ 735 unsigned int *subfamily_name_id, /* OUT. May be NULL */ 736 unsigned int *range_start, /* OUT. May be NULL */ 737 unsigned int *range_end /* OUT. May be NULL */) 738 { 739 const OT::GPOS &gpos = _get_gpos (face); 740 const hb_tag_t tag = HB_TAG ('s','i','z','e'); 741 742 unsigned int num_features = gpos.get_feature_count (); 743 for (unsigned int i = 0; i < num_features; i++) 744 { 745 if (tag == gpos.get_feature_tag (i)) 746 { 747 const OT::Feature &f = gpos.get_feature (i); 748 const OT::FeatureParamsSize ¶ms = f.get_feature_params ().get_size_params (tag); 749 750 if (params.designSize) 751 { 752 #define PARAM(a, A) if (a) *a = params.A 753 PARAM (design_size, designSize); 754 PARAM (subfamily_id, subfamilyID); 755 PARAM (subfamily_name_id, subfamilyNameID); 756 PARAM (range_start, rangeStart); 757 PARAM (range_end, rangeEnd); 758 #undef PARAM 759 760 return true; 761 } 762 } 763 } 764 765 #define PARAM(a, A) if (a) *a = 0 766 PARAM (design_size, designSize); 767 PARAM (subfamily_id, subfamilyID); 768 PARAM (subfamily_name_id, subfamilyNameID); 769 PARAM (range_start, rangeStart); 770 PARAM (range_end, rangeEnd); 771 #undef PARAM 772 773 return false; 774 } 775 776 777 /* 778 * Parts of different types are implemented here such that they have direct 779 * access to GSUB/GPOS lookups. 780 */ 781 782 783 struct GSUBProxy 784 { 785 static const unsigned int table_index = 0; 786 static const bool inplace = false; 787 typedef OT::SubstLookup Lookup; 788 789 GSUBProxy (hb_face_t *face) : 790 table (*hb_ot_layout_from_face (face)->gsub), 791 accels (hb_ot_layout_from_face (face)->gsub_accels) {} 792 793 const OT::GSUB &table; 794 const hb_ot_layout_lookup_accelerator_t *accels; 795 }; 796 797 struct GPOSProxy 798 { 799 static const unsigned int table_index = 1; 800 static const bool inplace = true; 801 typedef OT::PosLookup Lookup; 802 803 GPOSProxy (hb_face_t *face) : 804 table (*hb_ot_layout_from_face (face)->gpos), 805 accels (hb_ot_layout_from_face (face)->gpos_accels) {} 806 807 const OT::GPOS &table; 808 const hb_ot_layout_lookup_accelerator_t *accels; 809 }; 810 811 812 template <typename Lookup> 813 static inline bool apply_once (OT::hb_apply_context_t *c, 814 const Lookup &lookup) 815 { 816 if (!c->check_glyph_property (&c->buffer->cur(), c->lookup_props)) 817 return false; 818 return lookup.dispatch (c); 819 } 820 821 template <typename Proxy> 822 static inline bool 823 apply_string (OT::hb_apply_context_t *c, 824 const typename Proxy::Lookup &lookup, 825 const hb_ot_layout_lookup_accelerator_t &accel) 826 { 827 bool ret = false; 828 hb_buffer_t *buffer = c->buffer; 829 830 if (unlikely (!buffer->len || !c->lookup_mask)) 831 return false; 832 833 c->set_lookup (lookup); 834 835 if (likely (!lookup.is_reverse ())) 836 { 837 /* in/out forward substitution/positioning */ 838 if (Proxy::table_index == 0) 839 buffer->clear_output (); 840 buffer->idx = 0; 841 842 while (buffer->idx < buffer->len) 843 { 844 if (accel.digest.may_have (buffer->cur().codepoint) && 845 (buffer->cur().mask & c->lookup_mask) && 846 apply_once (c, lookup)) 847 ret = true; 848 else 849 buffer->next_glyph (); 850 } 851 if (ret) 852 { 853 if (!Proxy::inplace) 854 buffer->swap_buffers (); 855 else 856 assert (!buffer->has_separate_output ()); 857 } 858 } 859 else 860 { 861 /* in-place backward substitution/positioning */ 862 if (Proxy::table_index == 0) 863 buffer->remove_output (); 864 buffer->idx = buffer->len - 1; 865 do 866 { 867 if (accel.digest.may_have (buffer->cur().codepoint) && 868 (buffer->cur().mask & c->lookup_mask) && 869 apply_once (c, lookup)) 870 ret = true; 871 /* The reverse lookup doesn't "advance" cursor (for good reason). */ 872 buffer->idx--; 873 874 } 875 while ((int) buffer->idx >= 0); 876 } 877 878 return ret; 879 } 880 881 template <typename Proxy> 882 inline void hb_ot_map_t::apply (const Proxy &proxy, 883 const hb_ot_shape_plan_t *plan, 884 hb_font_t *font, 885 hb_buffer_t *buffer) const 886 { 887 const unsigned int table_index = proxy.table_index; 888 unsigned int i = 0; 889 OT::hb_apply_context_t c (table_index, font, buffer); 890 c.set_recurse_func (Proxy::Lookup::apply_recurse_func); 891 892 for (unsigned int stage_index = 0; stage_index < stages[table_index].len; stage_index++) { 893 const stage_map_t *stage = &stages[table_index][stage_index]; 894 for (; i < stage->last_lookup; i++) 895 { 896 unsigned int lookup_index = lookups[table_index][i].index; 897 c.set_lookup_mask (lookups[table_index][i].mask); 898 c.set_auto_zwj (lookups[table_index][i].auto_zwj); 899 apply_string<Proxy> (&c, 900 proxy.table.get_lookup (lookup_index), 901 proxy.accels[lookup_index]); 902 } 903 904 if (stage->pause_func) 905 { 906 buffer->clear_output (); 907 stage->pause_func (plan, font, buffer); 908 } 909 } 910 } 911 912 void hb_ot_map_t::substitute (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const 913 { 914 GSUBProxy proxy (font->face); 915 apply (proxy, plan, font, buffer); 916 } 917 918 void hb_ot_map_t::position (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const 919 { 920 GPOSProxy proxy (font->face); 921 apply (proxy, plan, font, buffer); 922 } 923 924 HB_INTERNAL void 925 hb_ot_layout_substitute_lookup (OT::hb_apply_context_t *c, 926 const OT::SubstLookup &lookup, 927 const hb_ot_layout_lookup_accelerator_t &accel) 928 { 929 apply_string<GSUBProxy> (c, lookup, accel); 930 } 931