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