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