hb-ot-layout.cc revision 49901862e36e1c153835877a9f1183729333bbbe
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 return 0; 433} 434 435static 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 460static 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 523static 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 573void 574hb_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 614void 615hb_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 653hb_bool_t 654hb_ot_layout_has_substitution (hb_face_t *face) 655{ 656 return &_get_gsub (face) != &OT::Null(OT::GSUB); 657} 658 659hb_bool_t 660hb_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 670hb_bool_t 671hb_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 685void 686hb_ot_layout_substitute_start (hb_font_t *font, hb_buffer_t *buffer) 687{ 688 OT::GSUB::substitute_start (font, buffer); 689} 690 691void 692hb_ot_layout_substitute_finish (hb_font_t *font, hb_buffer_t *buffer) 693{ 694 OT::GSUB::substitute_finish (font, buffer); 695} 696 697void 698hb_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 713hb_bool_t 714hb_ot_layout_has_positioning (hb_face_t *face) 715{ 716 return &_get_gpos (face) != &OT::Null(OT::GPOS); 717} 718 719void 720hb_ot_layout_position_start (hb_font_t *font, hb_buffer_t *buffer) 721{ 722 OT::GPOS::position_start (font, buffer); 723} 724 725void 726hb_ot_layout_position_finish (hb_font_t *font, hb_buffer_t *buffer) 727{ 728 OT::GPOS::position_finish (font, buffer); 729} 730 731hb_bool_t 732hb_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 783struct GSUBProxy 784{ 785 static const unsigned int table_index = 0; 786 typedef OT::SubstLookup Lookup; 787 788 GSUBProxy (hb_face_t *face) : 789 table (*hb_ot_layout_from_face (face)->gsub), 790 accels (hb_ot_layout_from_face (face)->gsub_accels) {} 791 792 const OT::GSUB &table; 793 const hb_ot_layout_lookup_accelerator_t *accels; 794}; 795 796struct GPOSProxy 797{ 798 static const unsigned int table_index = 1; 799 typedef OT::PosLookup Lookup; 800 801 GPOSProxy (hb_face_t *face) : 802 table (*hb_ot_layout_from_face (face)->gpos), 803 accels (hb_ot_layout_from_face (face)->gpos_accels) {} 804 805 const OT::GPOS &table; 806 const hb_ot_layout_lookup_accelerator_t *accels; 807}; 808 809 810template <typename Lookup> 811static inline bool apply_once (OT::hb_apply_context_t *c, 812 const Lookup &lookup) 813{ 814 if (!c->check_glyph_property (&c->buffer->cur(), c->lookup_props)) 815 return false; 816 return lookup.dispatch (c); 817} 818 819template <typename Proxy> 820static inline bool 821apply_string (OT::hb_apply_context_t *c, 822 const typename Proxy::Lookup &lookup, 823 const hb_ot_layout_lookup_accelerator_t &accel) 824{ 825 bool ret = false; 826 OT::hb_is_inplace_context_t inplace_c (c->face); 827 bool inplace = lookup.is_inplace (&inplace_c); 828 829 if (unlikely (!c->buffer->len || !c->lookup_mask)) 830 return false; 831 832 c->set_lookup (lookup); 833 834 if (likely (!lookup.is_reverse ())) 835 { 836 /* in/out forward substitution/positioning */ 837 if (Proxy::table_index == 0) 838 c->buffer->clear_output (); 839 c->buffer->idx = 0; 840 841 while (c->buffer->idx < c->buffer->len) 842 { 843 if (accel.digest.may_have (c->buffer->cur().codepoint) && 844 (c->buffer->cur().mask & c->lookup_mask) && 845 apply_once (c, lookup)) 846 ret = true; 847 else 848 c->buffer->next_glyph (); 849 } 850 if (ret) 851 { 852 if (!inplace) 853 c->buffer->swap_buffers (); 854 else 855 assert (!c->buffer->has_separate_output ()); 856 } 857 } 858 else 859 { 860 /* in-place backward substitution/positioning */ 861 if (Proxy::table_index == 0) 862 c->buffer->remove_output (); 863 c->buffer->idx = c->buffer->len - 1; 864 do 865 { 866 if (accel.digest.may_have (c->buffer->cur().codepoint) && 867 (c->buffer->cur().mask & c->lookup_mask) && 868 apply_once (c, lookup)) 869 ret = true; 870 /* The reverse lookup doesn't "advance" cursor (for good reason). */ 871 c->buffer->idx--; 872 873 } 874 while ((int) c->buffer->idx >= 0); 875 } 876 877 return ret; 878} 879 880template <typename Proxy> 881inline void hb_ot_map_t::apply (const Proxy &proxy, 882 const hb_ot_shape_plan_t *plan, 883 hb_font_t *font, 884 hb_buffer_t *buffer) const 885{ 886 const unsigned int table_index = proxy.table_index; 887 unsigned int i = 0; 888 OT::hb_apply_context_t c (table_index, font, buffer); 889 c.set_recurse_func (Proxy::Lookup::apply_recurse_func); 890 891 for (unsigned int stage_index = 0; stage_index < stages[table_index].len; stage_index++) { 892 const stage_map_t *stage = &stages[table_index][stage_index]; 893 for (; i < stage->last_lookup; i++) 894 { 895 unsigned int lookup_index = lookups[table_index][i].index; 896 c.set_lookup_mask (lookups[table_index][i].mask); 897 c.set_auto_zwj (lookups[table_index][i].auto_zwj); 898 apply_string<Proxy> (&c, 899 proxy.table.get_lookup (lookup_index), 900 proxy.accels[lookup_index]); 901 } 902 903 if (stage->pause_func) 904 { 905 buffer->clear_output (); 906 stage->pause_func (plan, font, buffer); 907 } 908 } 909} 910 911void hb_ot_map_t::substitute (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const 912{ 913 GSUBProxy proxy (font->face); 914 apply (proxy, plan, font, buffer); 915} 916 917void hb_ot_map_t::position (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const 918{ 919 GPOSProxy proxy (font->face); 920 apply (proxy, plan, font, buffer); 921} 922 923HB_INTERNAL void 924hb_ot_layout_substitute_lookup (OT::hb_apply_context_t *c, 925 const OT::SubstLookup &lookup, 926 const hb_ot_layout_lookup_accelerator_t &accel) 927{ 928 apply_string<GSUBProxy> (c, lookup, accel); 929} 930