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 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 37 #include <stdlib.h> 38 #include <string.h> 39 40 41 HB_SHAPER_DATA_ENSURE_DECLARE(ot, face) 42 43 hb_ot_layout_t * 44 _hb_ot_layout_create (hb_face_t *face) 45 { 46 hb_ot_layout_t *layout = (hb_ot_layout_t *) calloc (1, sizeof (hb_ot_layout_t)); 47 if (unlikely (!layout)) 48 return NULL; 49 50 layout->gdef_blob = OT::Sanitizer<OT::GDEF>::sanitize (face->reference_table (HB_OT_TAG_GDEF)); 51 layout->gdef = OT::Sanitizer<OT::GDEF>::lock_instance (layout->gdef_blob); 52 53 layout->gsub_blob = OT::Sanitizer<OT::GSUB>::sanitize (face->reference_table (HB_OT_TAG_GSUB)); 54 layout->gsub = OT::Sanitizer<OT::GSUB>::lock_instance (layout->gsub_blob); 55 56 layout->gpos_blob = OT::Sanitizer<OT::GPOS>::sanitize (face->reference_table (HB_OT_TAG_GPOS)); 57 layout->gpos = OT::Sanitizer<OT::GPOS>::lock_instance (layout->gpos_blob); 58 59 layout->gsub_lookup_count = layout->gsub->get_lookup_count (); 60 layout->gpos_lookup_count = layout->gpos->get_lookup_count (); 61 62 layout->gsub_digests = (hb_set_digest_t *) calloc (layout->gsub->get_lookup_count (), sizeof (hb_set_digest_t)); 63 layout->gpos_digests = (hb_set_digest_t *) calloc (layout->gpos->get_lookup_count (), sizeof (hb_set_digest_t)); 64 65 if (unlikely ((layout->gsub_lookup_count && !layout->gsub_digests) || 66 (layout->gpos_lookup_count && !layout->gpos_digests))) 67 { 68 _hb_ot_layout_destroy (layout); 69 return NULL; 70 } 71 72 for (unsigned int i = 0; i < layout->gsub_lookup_count; i++) 73 layout->gsub->get_lookup (i).add_coverage (&layout->gsub_digests[i]); 74 for (unsigned int i = 0; i < layout->gpos_lookup_count; i++) 75 layout->gpos->get_lookup (i).add_coverage (&layout->gpos_digests[i]); 76 77 return layout; 78 } 79 80 void 81 _hb_ot_layout_destroy (hb_ot_layout_t *layout) 82 { 83 hb_blob_destroy (layout->gdef_blob); 84 hb_blob_destroy (layout->gsub_blob); 85 hb_blob_destroy (layout->gpos_blob); 86 87 free (layout->gsub_digests); 88 free (layout->gpos_digests); 89 90 free (layout); 91 } 92 93 static inline const OT::GDEF& 94 _get_gdef (hb_face_t *face) 95 { 96 if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GDEF); 97 return *hb_ot_layout_from_face (face)->gdef; 98 } 99 static inline const OT::GSUB& 100 _get_gsub (hb_face_t *face) 101 { 102 if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GSUB); 103 return *hb_ot_layout_from_face (face)->gsub; 104 } 105 static inline const OT::GPOS& 106 _get_gpos (hb_face_t *face) 107 { 108 if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GPOS); 109 return *hb_ot_layout_from_face (face)->gpos; 110 } 111 112 113 /* 114 * GDEF 115 */ 116 117 hb_bool_t 118 hb_ot_layout_has_glyph_classes (hb_face_t *face) 119 { 120 return _get_gdef (face).has_glyph_classes (); 121 } 122 123 hb_ot_layout_glyph_class_t 124 hb_ot_layout_get_glyph_class (hb_face_t *face, 125 hb_codepoint_t glyph) 126 { 127 return (hb_ot_layout_glyph_class_t) _get_gdef (face).get_glyph_class (glyph); 128 } 129 130 void 131 hb_ot_layout_get_glyphs_in_class (hb_face_t *face, 132 hb_ot_layout_glyph_class_t klass, 133 hb_set_t *glyphs /* OUT */) 134 { 135 return _get_gdef (face).get_glyphs_in_class (klass, glyphs); 136 } 137 138 unsigned int 139 hb_ot_layout_get_attach_points (hb_face_t *face, 140 hb_codepoint_t glyph, 141 unsigned int start_offset, 142 unsigned int *point_count /* IN/OUT */, 143 unsigned int *point_array /* OUT */) 144 { 145 return _get_gdef (face).get_attach_points (glyph, start_offset, point_count, point_array); 146 } 147 148 unsigned int 149 hb_ot_layout_get_ligature_carets (hb_font_t *font, 150 hb_direction_t direction, 151 hb_codepoint_t glyph, 152 unsigned int start_offset, 153 unsigned int *caret_count /* IN/OUT */, 154 int *caret_array /* OUT */) 155 { 156 return _get_gdef (font->face).get_lig_carets (font, direction, glyph, start_offset, caret_count, caret_array); 157 } 158 159 160 /* 161 * GSUB/GPOS 162 */ 163 164 static const OT::GSUBGPOS& 165 get_gsubgpos_table (hb_face_t *face, 166 hb_tag_t table_tag) 167 { 168 switch (table_tag) { 169 case HB_OT_TAG_GSUB: return _get_gsub (face); 170 case HB_OT_TAG_GPOS: return _get_gpos (face); 171 default: return OT::Null(OT::GSUBGPOS); 172 } 173 } 174 175 176 unsigned int 177 hb_ot_layout_table_get_script_tags (hb_face_t *face, 178 hb_tag_t table_tag, 179 unsigned int start_offset, 180 unsigned int *script_count /* IN/OUT */, 181 hb_tag_t *script_tags /* OUT */) 182 { 183 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 184 185 return g.get_script_tags (start_offset, script_count, script_tags); 186 } 187 188 #define HB_OT_TAG_LATIN_SCRIPT HB_TAG ('l', 'a', 't', 'n') 189 190 hb_bool_t 191 hb_ot_layout_table_find_script (hb_face_t *face, 192 hb_tag_t table_tag, 193 hb_tag_t script_tag, 194 unsigned int *script_index) 195 { 196 ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX); 197 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 198 199 if (g.find_script_index (script_tag, script_index)) 200 return true; 201 202 /* try finding 'DFLT' */ 203 if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index)) 204 return false; 205 206 /* try with 'dflt'; MS site has had typos and many fonts use it now :(. 207 * including many versions of DejaVu Sans Mono! */ 208 if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index)) 209 return false; 210 211 /* try with 'latn'; some old fonts put their features there even though 212 they're really trying to support Thai, for example :( */ 213 if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, script_index)) 214 return false; 215 216 if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX; 217 return false; 218 } 219 220 hb_bool_t 221 hb_ot_layout_table_choose_script (hb_face_t *face, 222 hb_tag_t table_tag, 223 const hb_tag_t *script_tags, 224 unsigned int *script_index, 225 hb_tag_t *chosen_script) 226 { 227 ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX); 228 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 229 230 while (*script_tags) 231 { 232 if (g.find_script_index (*script_tags, script_index)) { 233 if (chosen_script) 234 *chosen_script = *script_tags; 235 return true; 236 } 237 script_tags++; 238 } 239 240 /* try finding 'DFLT' */ 241 if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index)) { 242 if (chosen_script) 243 *chosen_script = HB_OT_TAG_DEFAULT_SCRIPT; 244 return false; 245 } 246 247 /* try with 'dflt'; MS site has had typos and many fonts use it now :( */ 248 if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index)) { 249 if (chosen_script) 250 *chosen_script = HB_OT_TAG_DEFAULT_LANGUAGE; 251 return false; 252 } 253 254 /* try with 'latn'; some old fonts put their features there even though 255 they're really trying to support Thai, for example :( */ 256 if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, script_index)) { 257 if (chosen_script) 258 *chosen_script = HB_OT_TAG_LATIN_SCRIPT; 259 return false; 260 } 261 262 if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX; 263 if (chosen_script) 264 *chosen_script = HB_OT_LAYOUT_NO_SCRIPT_INDEX; 265 return false; 266 } 267 268 unsigned int 269 hb_ot_layout_table_get_feature_tags (hb_face_t *face, 270 hb_tag_t table_tag, 271 unsigned int start_offset, 272 unsigned int *feature_count /* IN/OUT */, 273 hb_tag_t *feature_tags /* OUT */) 274 { 275 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 276 277 return g.get_feature_tags (start_offset, feature_count, feature_tags); 278 } 279 280 281 unsigned int 282 hb_ot_layout_script_get_language_tags (hb_face_t *face, 283 hb_tag_t table_tag, 284 unsigned int script_index, 285 unsigned int start_offset, 286 unsigned int *language_count /* IN/OUT */, 287 hb_tag_t *language_tags /* OUT */) 288 { 289 const OT::Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index); 290 291 return s.get_lang_sys_tags (start_offset, language_count, language_tags); 292 } 293 294 hb_bool_t 295 hb_ot_layout_script_find_language (hb_face_t *face, 296 hb_tag_t table_tag, 297 unsigned int script_index, 298 hb_tag_t language_tag, 299 unsigned int *language_index) 300 { 301 ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX); 302 const OT::Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index); 303 304 if (s.find_lang_sys_index (language_tag, language_index)) 305 return true; 306 307 /* try with 'dflt'; MS site has had typos and many fonts use it now :( */ 308 if (s.find_lang_sys_index (HB_OT_TAG_DEFAULT_LANGUAGE, language_index)) 309 return false; 310 311 if (language_index) *language_index = HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX; 312 return false; 313 } 314 315 hb_bool_t 316 hb_ot_layout_language_get_required_feature_index (hb_face_t *face, 317 hb_tag_t table_tag, 318 unsigned int script_index, 319 unsigned int language_index, 320 unsigned int *feature_index) 321 { 322 const OT::LangSys &l = get_gsubgpos_table (face, table_tag).get_script (script_index).get_lang_sys (language_index); 323 324 if (feature_index) *feature_index = l.get_required_feature_index (); 325 326 return l.has_required_feature (); 327 } 328 329 unsigned int 330 hb_ot_layout_language_get_feature_indexes (hb_face_t *face, 331 hb_tag_t table_tag, 332 unsigned int script_index, 333 unsigned int language_index, 334 unsigned int start_offset, 335 unsigned int *feature_count /* IN/OUT */, 336 unsigned int *feature_indexes /* OUT */) 337 { 338 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 339 const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index); 340 341 return l.get_feature_indexes (start_offset, feature_count, feature_indexes); 342 } 343 344 unsigned int 345 hb_ot_layout_language_get_feature_tags (hb_face_t *face, 346 hb_tag_t table_tag, 347 unsigned int script_index, 348 unsigned int language_index, 349 unsigned int start_offset, 350 unsigned int *feature_count /* IN/OUT */, 351 hb_tag_t *feature_tags /* OUT */) 352 { 353 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 354 const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index); 355 356 ASSERT_STATIC (sizeof (unsigned int) == sizeof (hb_tag_t)); 357 unsigned int ret = l.get_feature_indexes (start_offset, feature_count, (unsigned int *) feature_tags); 358 359 if (feature_tags) { 360 unsigned int count = *feature_count; 361 for (unsigned int i = 0; i < count; i++) 362 feature_tags[i] = g.get_feature_tag ((unsigned int) feature_tags[i]); 363 } 364 365 return ret; 366 } 367 368 369 hb_bool_t 370 hb_ot_layout_language_find_feature (hb_face_t *face, 371 hb_tag_t table_tag, 372 unsigned int script_index, 373 unsigned int language_index, 374 hb_tag_t feature_tag, 375 unsigned int *feature_index) 376 { 377 ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX); 378 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 379 const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index); 380 381 unsigned int num_features = l.get_feature_count (); 382 for (unsigned int i = 0; i < num_features; i++) { 383 unsigned int f_index = l.get_feature_index (i); 384 385 if (feature_tag == g.get_feature_tag (f_index)) { 386 if (feature_index) *feature_index = f_index; 387 return true; 388 } 389 } 390 391 if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX; 392 return false; 393 } 394 395 unsigned int 396 hb_ot_layout_feature_get_lookups (hb_face_t *face, 397 hb_tag_t table_tag, 398 unsigned int feature_index, 399 unsigned int start_offset, 400 unsigned int *lookup_count /* IN/OUT */, 401 unsigned int *lookup_indexes /* OUT */) 402 { 403 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 404 const OT::Feature &f = g.get_feature (feature_index); 405 406 return f.get_lookup_indexes (start_offset, lookup_count, lookup_indexes); 407 } 408 409 static void 410 _hb_ot_layout_collect_lookups_lookups (hb_face_t *face, 411 hb_tag_t table_tag, 412 unsigned int feature_index, 413 hb_set_t *lookup_indexes /* OUT */) 414 { 415 unsigned int lookup_indices[32]; 416 unsigned int offset, len; 417 418 offset = 0; 419 do { 420 len = ARRAY_LENGTH (lookup_indices); 421 hb_ot_layout_feature_get_lookups (face, 422 table_tag, 423 feature_index, 424 offset, &len, 425 lookup_indices); 426 427 for (unsigned int i = 0; i < len; i++) 428 lookup_indexes->add (lookup_indices[i]); 429 430 offset += len; 431 } while (len == ARRAY_LENGTH (lookup_indices)); 432 } 433 434 static void 435 _hb_ot_layout_collect_lookups_features (hb_face_t *face, 436 hb_tag_t table_tag, 437 unsigned int script_index, 438 unsigned int language_index, 439 const hb_tag_t *features, 440 hb_set_t *lookup_indexes /* OUT */) 441 { 442 unsigned int required_feature_index; 443 if (hb_ot_layout_language_get_required_feature_index (face, 444 table_tag, 445 script_index, 446 language_index, 447 &required_feature_index)) 448 _hb_ot_layout_collect_lookups_lookups (face, 449 table_tag, 450 required_feature_index, 451 lookup_indexes); 452 453 if (!features) 454 { 455 /* All features */ 456 unsigned int feature_indices[32]; 457 unsigned int offset, len; 458 459 offset = 0; 460 do { 461 len = ARRAY_LENGTH (feature_indices); 462 hb_ot_layout_language_get_feature_indexes (face, 463 table_tag, 464 script_index, 465 language_index, 466 offset, &len, 467 feature_indices); 468 469 for (unsigned int i = 0; i < len; i++) 470 _hb_ot_layout_collect_lookups_lookups (face, 471 table_tag, 472 feature_indices[i], 473 lookup_indexes); 474 475 offset += len; 476 } while (len == ARRAY_LENGTH (feature_indices)); 477 } 478 else 479 { 480 for (; *features; features++) 481 { 482 unsigned int feature_index; 483 if (hb_ot_layout_language_find_feature (face, 484 table_tag, 485 script_index, 486 language_index, 487 *features, 488 &feature_index)) 489 _hb_ot_layout_collect_lookups_lookups (face, 490 table_tag, 491 feature_index, 492 lookup_indexes); 493 } 494 } 495 } 496 497 static void 498 _hb_ot_layout_collect_lookups_languages (hb_face_t *face, 499 hb_tag_t table_tag, 500 unsigned int script_index, 501 const hb_tag_t *languages, 502 const hb_tag_t *features, 503 hb_set_t *lookup_indexes /* OUT */) 504 { 505 _hb_ot_layout_collect_lookups_features (face, 506 table_tag, 507 script_index, 508 HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX, 509 features, 510 lookup_indexes); 511 512 if (!languages) 513 { 514 /* All languages */ 515 unsigned int count = hb_ot_layout_script_get_language_tags (face, 516 table_tag, 517 script_index, 518 0, NULL, NULL); 519 for (unsigned int language_index = 0; language_index < count; language_index++) 520 _hb_ot_layout_collect_lookups_features (face, 521 table_tag, 522 script_index, 523 language_index, 524 features, 525 lookup_indexes); 526 } 527 else 528 { 529 for (; *languages; languages++) 530 { 531 unsigned int language_index; 532 if (hb_ot_layout_script_find_language (face, 533 table_tag, 534 script_index, 535 *languages, 536 &language_index)) 537 _hb_ot_layout_collect_lookups_features (face, 538 table_tag, 539 script_index, 540 language_index, 541 features, 542 lookup_indexes); 543 } 544 } 545 } 546 547 void 548 hb_ot_layout_collect_lookups (hb_face_t *face, 549 hb_tag_t table_tag, 550 const hb_tag_t *scripts, 551 const hb_tag_t *languages, 552 const hb_tag_t *features, 553 hb_set_t *lookup_indexes /* OUT */) 554 { 555 if (!scripts) 556 { 557 /* All scripts */ 558 unsigned int count = hb_ot_layout_table_get_script_tags (face, 559 table_tag, 560 0, NULL, NULL); 561 for (unsigned int script_index = 0; script_index < count; script_index++) 562 _hb_ot_layout_collect_lookups_languages (face, 563 table_tag, 564 script_index, 565 languages, 566 features, 567 lookup_indexes); 568 } 569 else 570 { 571 for (; *scripts; scripts++) 572 { 573 unsigned int script_index; 574 if (hb_ot_layout_table_find_script (face, 575 table_tag, 576 *scripts, 577 &script_index)) 578 _hb_ot_layout_collect_lookups_languages (face, 579 table_tag, 580 script_index, 581 languages, 582 features, 583 lookup_indexes); 584 } 585 } 586 } 587 588 void 589 hb_ot_layout_lookup_collect_glyphs (hb_face_t *face, 590 hb_tag_t table_tag, 591 unsigned int lookup_index, 592 hb_set_t *glyphs_before, /* OUT. May be NULL */ 593 hb_set_t *glyphs_input, /* OUT. May be NULL */ 594 hb_set_t *glyphs_after, /* OUT. May be NULL */ 595 hb_set_t *glyphs_output /* OUT. May be NULL */) 596 { 597 if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return; 598 599 OT::hb_collect_glyphs_context_t c (face, 600 glyphs_before, 601 glyphs_input, 602 glyphs_after, 603 glyphs_output); 604 605 switch (table_tag) 606 { 607 case HB_OT_TAG_GSUB: 608 { 609 const OT::SubstLookup& l = hb_ot_layout_from_face (face)->gsub->get_lookup (lookup_index); 610 l.collect_glyphs_lookup (&c); 611 return; 612 } 613 case HB_OT_TAG_GPOS: 614 { 615 const OT::PosLookup& l = hb_ot_layout_from_face (face)->gpos->get_lookup (lookup_index); 616 l.collect_glyphs_lookup (&c); 617 return; 618 } 619 } 620 } 621 622 623 /* 624 * OT::GSUB 625 */ 626 627 hb_bool_t 628 hb_ot_layout_has_substitution (hb_face_t *face) 629 { 630 return &_get_gsub (face) != &OT::Null(OT::GSUB); 631 } 632 633 hb_bool_t 634 hb_ot_layout_lookup_would_substitute (hb_face_t *face, 635 unsigned int lookup_index, 636 const hb_codepoint_t *glyphs, 637 unsigned int glyphs_length, 638 hb_bool_t zero_context) 639 { 640 if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return false; 641 return hb_ot_layout_lookup_would_substitute_fast (face, lookup_index, glyphs, glyphs_length, zero_context); 642 } 643 644 hb_bool_t 645 hb_ot_layout_lookup_would_substitute_fast (hb_face_t *face, 646 unsigned int lookup_index, 647 const hb_codepoint_t *glyphs, 648 unsigned int glyphs_length, 649 hb_bool_t zero_context) 650 { 651 if (unlikely (lookup_index >= hb_ot_layout_from_face (face)->gsub_lookup_count)) return false; 652 OT::hb_would_apply_context_t c (face, glyphs, glyphs_length, zero_context); 653 654 const OT::SubstLookup& l = hb_ot_layout_from_face (face)->gsub->get_lookup (lookup_index); 655 656 return l.would_apply (&c, &hb_ot_layout_from_face (face)->gsub_digests[lookup_index]); 657 } 658 659 void 660 hb_ot_layout_substitute_start (hb_font_t *font, hb_buffer_t *buffer) 661 { 662 OT::GSUB::substitute_start (font, buffer); 663 } 664 665 hb_bool_t 666 hb_ot_layout_substitute_lookup (hb_font_t *font, 667 hb_buffer_t *buffer, 668 unsigned int lookup_index, 669 hb_mask_t mask, 670 hb_bool_t auto_zwj) 671 { 672 if (unlikely (lookup_index >= hb_ot_layout_from_face (font->face)->gsub_lookup_count)) return false; 673 674 OT::hb_apply_context_t c (0, font, buffer, mask, auto_zwj); 675 676 const OT::SubstLookup& l = hb_ot_layout_from_face (font->face)->gsub->get_lookup (lookup_index); 677 678 return l.apply_string (&c, &hb_ot_layout_from_face (font->face)->gsub_digests[lookup_index]); 679 } 680 681 void 682 hb_ot_layout_substitute_finish (hb_font_t *font, hb_buffer_t *buffer) 683 { 684 OT::GSUB::substitute_finish (font, buffer); 685 } 686 687 void 688 hb_ot_layout_lookup_substitute_closure (hb_face_t *face, 689 unsigned int lookup_index, 690 hb_set_t *glyphs) 691 { 692 OT::hb_closure_context_t c (face, glyphs); 693 694 const OT::SubstLookup& l = _get_gsub (face).get_lookup (lookup_index); 695 696 l.closure (&c); 697 } 698 699 /* 700 * OT::GPOS 701 */ 702 703 hb_bool_t 704 hb_ot_layout_has_positioning (hb_face_t *face) 705 { 706 return &_get_gpos (face) != &OT::Null(OT::GPOS); 707 } 708 709 void 710 hb_ot_layout_position_start (hb_font_t *font, hb_buffer_t *buffer) 711 { 712 OT::GPOS::position_start (font, buffer); 713 } 714 715 hb_bool_t 716 hb_ot_layout_position_lookup (hb_font_t *font, 717 hb_buffer_t *buffer, 718 unsigned int lookup_index, 719 hb_mask_t mask, 720 hb_bool_t auto_zwj) 721 { 722 if (unlikely (lookup_index >= hb_ot_layout_from_face (font->face)->gpos_lookup_count)) return false; 723 724 OT::hb_apply_context_t c (1, font, buffer, mask, auto_zwj); 725 726 const OT::PosLookup& l = hb_ot_layout_from_face (font->face)->gpos->get_lookup (lookup_index); 727 728 return l.apply_string (&c, &hb_ot_layout_from_face (font->face)->gpos_digests[lookup_index]); 729 } 730 731 void 732 hb_ot_layout_position_finish (hb_font_t *font, hb_buffer_t *buffer) 733 { 734 OT::GPOS::position_finish (font, buffer); 735 } 736 737 hb_bool_t 738 hb_ot_layout_get_size_params (hb_face_t *face, 739 unsigned int *design_size, /* OUT. May be NULL */ 740 unsigned int *subfamily_id, /* OUT. May be NULL */ 741 unsigned int *subfamily_name_id, /* OUT. May be NULL */ 742 unsigned int *range_start, /* OUT. May be NULL */ 743 unsigned int *range_end /* OUT. May be NULL */) 744 { 745 const OT::GPOS &gpos = _get_gpos (face); 746 const hb_tag_t tag = HB_TAG ('s','i','z','e'); 747 748 unsigned int num_features = gpos.get_feature_count (); 749 for (unsigned int i = 0; i < num_features; i++) 750 { 751 if (tag == gpos.get_feature_tag (i)) 752 { 753 const OT::Feature &f = gpos.get_feature (i); 754 const OT::FeatureParamsSize ¶ms = f.get_feature_params ().get_size_params (tag); 755 756 if (params.designSize) 757 { 758 #define PARAM(a, A) if (a) *a = params.A 759 PARAM (design_size, designSize); 760 PARAM (subfamily_id, subfamilyID); 761 PARAM (subfamily_name_id, subfamilyNameID); 762 PARAM (range_start, rangeStart); 763 PARAM (range_end, rangeEnd); 764 #undef PARAM 765 766 return true; 767 } 768 } 769 } 770 771 #define PARAM(a, A) if (a) *a = 0 772 PARAM (design_size, designSize); 773 PARAM (subfamily_id, subfamilyID); 774 PARAM (subfamily_name_id, subfamilyNameID); 775 PARAM (range_start, rangeStart); 776 PARAM (range_end, rangeEnd); 777 #undef PARAM 778 779 return false; 780 } 781