hb-ot-layout-gsub-table.hh revision 29416833584d7831ece84aaeada6f5ebba7828c0
1/* 2 * Copyright © 2007,2008,2009,2010 Red Hat, Inc. 3 * Copyright © 2010,2012 Google, Inc. 4 * 5 * This is part of HarfBuzz, a text shaping library. 6 * 7 * Permission is hereby granted, without written agreement and without 8 * license or royalty fees, to use, copy, modify, and distribute this 9 * software and its documentation for any purpose, provided that the 10 * above copyright notice and the following two paragraphs appear in 11 * all copies of this software. 12 * 13 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR 14 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 15 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN 16 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 17 * DAMAGE. 18 * 19 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, 20 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 21 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS 22 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO 23 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 24 * 25 * Red Hat Author(s): Behdad Esfahbod 26 * Google Author(s): Behdad Esfahbod 27 */ 28 29#ifndef HB_OT_LAYOUT_GSUB_TABLE_HH 30#define HB_OT_LAYOUT_GSUB_TABLE_HH 31 32#include "hb-ot-layout-gsubgpos-private.hh" 33 34 35namespace OT { 36 37 38struct SingleSubstFormat1 39{ 40 friend struct SingleSubst; 41 42 private: 43 44 inline void closure (hb_closure_context_t *c) const 45 { 46 TRACE_CLOSURE (); 47 Coverage::Iter iter; 48 for (iter.init (this+coverage); iter.more (); iter.next ()) { 49 hb_codepoint_t glyph_id = iter.get_glyph (); 50 if (c->glyphs->has (glyph_id)) 51 c->glyphs->add ((glyph_id + deltaGlyphID) & 0xFFFF); 52 } 53 } 54 55 inline const Coverage &get_coverage (void) const 56 { 57 return this+coverage; 58 } 59 60 inline bool apply (hb_apply_context_t *c) const 61 { 62 TRACE_APPLY (); 63 hb_codepoint_t glyph_id = c->buffer->cur().codepoint; 64 unsigned int index = (this+coverage) (glyph_id); 65 if (likely (index == NOT_COVERED)) return TRACE_RETURN (false); 66 67 /* According to the Adobe Annotated OpenType Suite, result is always 68 * limited to 16bit. */ 69 glyph_id = (glyph_id + deltaGlyphID) & 0xFFFF; 70 c->replace_glyph (glyph_id); 71 72 return TRACE_RETURN (true); 73 } 74 75 inline bool serialize (hb_serialize_context_t *c, 76 const USHORT *glyphs, 77 unsigned int num_glyphs, 78 unsigned int delta) 79 { 80 TRACE_SERIALIZE (); 81 if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false); 82 if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return TRACE_RETURN (false); 83 deltaGlyphID.set (delta); /* TODO(serilaize) overflow? */ 84 return TRACE_RETURN (true); 85 } 86 87 inline bool sanitize (hb_sanitize_context_t *c) { 88 TRACE_SANITIZE (); 89 return TRACE_RETURN (coverage.sanitize (c, this) && deltaGlyphID.sanitize (c)); 90 } 91 92 protected: 93 USHORT format; /* Format identifier--format = 1 */ 94 OffsetTo<Coverage> 95 coverage; /* Offset to Coverage table--from 96 * beginning of Substitution table */ 97 SHORT deltaGlyphID; /* Add to original GlyphID to get 98 * substitute GlyphID */ 99 public: 100 DEFINE_SIZE_STATIC (6); 101}; 102 103struct SingleSubstFormat2 104{ 105 friend struct SingleSubst; 106 107 private: 108 109 inline void closure (hb_closure_context_t *c) const 110 { 111 TRACE_CLOSURE (); 112 Coverage::Iter iter; 113 for (iter.init (this+coverage); iter.more (); iter.next ()) { 114 if (c->glyphs->has (iter.get_glyph ())) 115 c->glyphs->add (substitute[iter.get_coverage ()]); 116 } 117 } 118 119 inline const Coverage &get_coverage (void) const 120 { 121 return this+coverage; 122 } 123 124 inline bool apply (hb_apply_context_t *c) const 125 { 126 TRACE_APPLY (); 127 hb_codepoint_t glyph_id = c->buffer->cur().codepoint; 128 unsigned int index = (this+coverage) (glyph_id); 129 if (likely (index == NOT_COVERED)) return TRACE_RETURN (false); 130 131 if (unlikely (index >= substitute.len)) return TRACE_RETURN (false); 132 133 glyph_id = substitute[index]; 134 c->replace_glyph (glyph_id); 135 136 return TRACE_RETURN (true); 137 } 138 139 inline bool serialize (hb_serialize_context_t *c, 140 const USHORT *glyphs, 141 const USHORT *substitutes, 142 unsigned int num_glyphs) 143 { 144 TRACE_SERIALIZE (); 145 if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false); 146 if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return TRACE_RETURN (false); 147 if (unlikely (!substitute.serialize (c, substitutes, num_glyphs))) return TRACE_RETURN (false); 148 return TRACE_RETURN (true); 149 } 150 151 inline bool sanitize (hb_sanitize_context_t *c) { 152 TRACE_SANITIZE (); 153 return TRACE_RETURN (coverage.sanitize (c, this) && substitute.sanitize (c)); 154 } 155 156 protected: 157 USHORT format; /* Format identifier--format = 2 */ 158 OffsetTo<Coverage> 159 coverage; /* Offset to Coverage table--from 160 * beginning of Substitution table */ 161 ArrayOf<GlyphID> 162 substitute; /* Array of substitute 163 * GlyphIDs--ordered by Coverage Index */ 164 public: 165 DEFINE_SIZE_ARRAY (6, substitute); 166}; 167 168struct SingleSubst 169{ 170 friend struct SubstLookupSubTable; 171 172 private: 173 174 inline void closure (hb_closure_context_t *c) const 175 { 176 TRACE_CLOSURE (); 177 switch (u.format) { 178 case 1: u.format1.closure (c); break; 179 case 2: u.format2.closure (c); break; 180 default: break; 181 } 182 } 183 184 inline const Coverage &get_coverage (void) const 185 { 186 switch (u.format) { 187 case 1: return u.format1.get_coverage (); 188 case 2: return u.format2.get_coverage (); 189 default:return Null(Coverage); 190 } 191 } 192 193 inline bool apply (hb_apply_context_t *c) const 194 { 195 TRACE_APPLY (); 196 switch (u.format) { 197 case 1: return TRACE_RETURN (u.format1.apply (c)); 198 case 2: return TRACE_RETURN (u.format2.apply (c)); 199 default:return TRACE_RETURN (false); 200 } 201 } 202 203 inline bool serialize (hb_serialize_context_t *c, 204 const USHORT *glyphs, 205 const USHORT *substitutes, 206 unsigned int num_glyphs) 207 { 208 TRACE_SERIALIZE (); 209 if (unlikely (!c->extend_min (u.format))) return TRACE_RETURN (false); 210 unsigned int format = 2; 211 unsigned int delta; 212 if (num_glyphs) { 213 format = 1; 214 /* TODO(serialize) check for wrap-around */ 215 delta = substitutes[0] - glyphs[0]; 216 for (unsigned int i = 1; i < num_glyphs; i++) 217 if (delta != substitutes[i] - glyphs[i]) { 218 format = 2; 219 break; 220 } 221 } 222 u.format.set (format); 223 switch (u.format) { 224 case 1: return TRACE_RETURN (u.format1.serialize (c, glyphs, num_glyphs, delta)); 225 case 2: return TRACE_RETURN (u.format2.serialize (c, glyphs, substitutes, num_glyphs)); 226 default:return TRACE_RETURN (false); 227 } 228 } 229 230 inline bool sanitize (hb_sanitize_context_t *c) { 231 TRACE_SANITIZE (); 232 if (!u.format.sanitize (c)) return TRACE_RETURN (false); 233 switch (u.format) { 234 case 1: return TRACE_RETURN (u.format1.sanitize (c)); 235 case 2: return TRACE_RETURN (u.format2.sanitize (c)); 236 default:return TRACE_RETURN (true); 237 } 238 } 239 240 protected: 241 union { 242 USHORT format; /* Format identifier */ 243 SingleSubstFormat1 format1; 244 SingleSubstFormat2 format2; 245 } u; 246}; 247 248 249struct Sequence 250{ 251 friend struct MultipleSubstFormat1; 252 253 private: 254 255 inline void closure (hb_closure_context_t *c) const 256 { 257 TRACE_CLOSURE (); 258 unsigned int count = substitute.len; 259 for (unsigned int i = 0; i < count; i++) 260 c->glyphs->add (substitute[i]); 261 } 262 263 inline bool apply (hb_apply_context_t *c) const 264 { 265 TRACE_APPLY (); 266 if (unlikely (!substitute.len)) return TRACE_RETURN (false); 267 268 unsigned int klass = c->property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE ? HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH : 0; 269 unsigned int count = substitute.len; 270 for (unsigned int i = 0; i < count; i++) { 271 set_lig_props_for_component (c->buffer->cur(), i); 272 c->output_glyph (substitute.array[i], klass); 273 } 274 c->buffer->skip_glyph (); 275 276 return TRACE_RETURN (true); 277 } 278 279 inline bool serialize (hb_serialize_context_t *c, 280 const USHORT *glyphs, 281 unsigned int num_glyphs) 282 { 283 TRACE_SERIALIZE (); 284 if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false); 285 if (unlikely (!substitute.serialize (c, glyphs, num_glyphs))) return TRACE_RETURN (false); 286 return TRACE_RETURN (true); 287 } 288 289 public: 290 inline bool sanitize (hb_sanitize_context_t *c) { 291 TRACE_SANITIZE (); 292 return TRACE_RETURN (substitute.sanitize (c)); 293 } 294 295 protected: 296 ArrayOf<GlyphID> 297 substitute; /* String of GlyphIDs to substitute */ 298 public: 299 DEFINE_SIZE_ARRAY (2, substitute); 300}; 301 302struct MultipleSubstFormat1 303{ 304 friend struct MultipleSubst; 305 306 private: 307 308 inline void closure (hb_closure_context_t *c) const 309 { 310 TRACE_CLOSURE (); 311 Coverage::Iter iter; 312 for (iter.init (this+coverage); iter.more (); iter.next ()) { 313 if (c->glyphs->has (iter.get_glyph ())) 314 (this+sequence[iter.get_coverage ()]).closure (c); 315 } 316 } 317 318 inline const Coverage &get_coverage (void) const 319 { 320 return this+coverage; 321 } 322 323 inline bool apply (hb_apply_context_t *c) const 324 { 325 TRACE_APPLY (); 326 327 unsigned int index = (this+coverage) (c->buffer->cur().codepoint); 328 if (likely (index == NOT_COVERED)) return TRACE_RETURN (false); 329 330 return TRACE_RETURN ((this+sequence[index]).apply (c)); 331 } 332 333 inline bool serialize (hb_serialize_context_t *c, 334 const USHORT *glyphs, 335 unsigned int *substitute_len_list, 336 unsigned int num_glyphs, 337 const USHORT *substitute_glyphs_list) 338 { 339 TRACE_SERIALIZE (); 340 if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false); 341 if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return TRACE_RETURN (false); 342 if (unlikely (!sequence.serialize (c, num_glyphs))) return TRACE_RETURN (false); 343 for (unsigned int i = 0; i < num_glyphs; i++) { 344 if (unlikely (!sequence[i].serialize (c, this).serialize (c, substitute_glyphs_list, substitute_len_list[i]))) return TRACE_RETURN (false); 345 substitute_glyphs_list += substitute_len_list[i]; 346 } 347 return TRACE_RETURN (true); 348 } 349 350 inline bool sanitize (hb_sanitize_context_t *c) { 351 TRACE_SANITIZE (); 352 return TRACE_RETURN (coverage.sanitize (c, this) && sequence.sanitize (c, this)); 353 } 354 355 protected: 356 USHORT format; /* Format identifier--format = 1 */ 357 OffsetTo<Coverage> 358 coverage; /* Offset to Coverage table--from 359 * beginning of Substitution table */ 360 OffsetArrayOf<Sequence> 361 sequence; /* Array of Sequence tables 362 * ordered by Coverage Index */ 363 public: 364 DEFINE_SIZE_ARRAY (6, sequence); 365}; 366 367struct MultipleSubst 368{ 369 friend struct SubstLookupSubTable; 370 371 private: 372 373 inline void closure (hb_closure_context_t *c) const 374 { 375 TRACE_CLOSURE (); 376 switch (u.format) { 377 case 1: u.format1.closure (c); break; 378 default: break; 379 } 380 } 381 382 inline const Coverage &get_coverage (void) const 383 { 384 switch (u.format) { 385 case 1: return u.format1.get_coverage (); 386 default:return Null(Coverage); 387 } 388 } 389 390 inline bool apply (hb_apply_context_t *c) const 391 { 392 TRACE_APPLY (); 393 switch (u.format) { 394 case 1: return TRACE_RETURN (u.format1.apply (c)); 395 default:return TRACE_RETURN (false); 396 } 397 } 398 399 inline bool serialize (hb_serialize_context_t *c, 400 const USHORT *glyphs, 401 unsigned int *substitute_len_list, 402 unsigned int num_glyphs, 403 const USHORT *substitute_glyphs_list) 404 { 405 TRACE_SERIALIZE (); 406 if (unlikely (!c->extend_min (u.format))) return TRACE_RETURN (false); 407 unsigned int format = 1; 408 u.format.set (format); 409 switch (u.format) { 410 case 1: return TRACE_RETURN (u.format1.serialize (c, glyphs, substitute_len_list, num_glyphs, substitute_glyphs_list)); 411 default:return TRACE_RETURN (false); 412 } 413 } 414 415 inline bool sanitize (hb_sanitize_context_t *c) { 416 TRACE_SANITIZE (); 417 if (!u.format.sanitize (c)) return TRACE_RETURN (false); 418 switch (u.format) { 419 case 1: return TRACE_RETURN (u.format1.sanitize (c)); 420 default:return TRACE_RETURN (true); 421 } 422 } 423 424 protected: 425 union { 426 USHORT format; /* Format identifier */ 427 MultipleSubstFormat1 format1; 428 } u; 429}; 430 431 432typedef ArrayOf<GlyphID> AlternateSet; /* Array of alternate GlyphIDs--in 433 * arbitrary order */ 434 435struct AlternateSubstFormat1 436{ 437 friend struct AlternateSubst; 438 439 private: 440 441 inline void closure (hb_closure_context_t *c) const 442 { 443 TRACE_CLOSURE (); 444 Coverage::Iter iter; 445 for (iter.init (this+coverage); iter.more (); iter.next ()) { 446 if (c->glyphs->has (iter.get_glyph ())) { 447 const AlternateSet &alt_set = this+alternateSet[iter.get_coverage ()]; 448 unsigned int count = alt_set.len; 449 for (unsigned int i = 0; i < count; i++) 450 c->glyphs->add (alt_set[i]); 451 } 452 } 453 } 454 455 inline const Coverage &get_coverage (void) const 456 { 457 return this+coverage; 458 } 459 460 inline bool apply (hb_apply_context_t *c) const 461 { 462 TRACE_APPLY (); 463 hb_codepoint_t glyph_id = c->buffer->cur().codepoint; 464 465 unsigned int index = (this+coverage) (glyph_id); 466 if (likely (index == NOT_COVERED)) return TRACE_RETURN (false); 467 468 const AlternateSet &alt_set = this+alternateSet[index]; 469 470 if (unlikely (!alt_set.len)) return TRACE_RETURN (false); 471 472 hb_mask_t glyph_mask = c->buffer->cur().mask; 473 hb_mask_t lookup_mask = c->lookup_mask; 474 475 /* Note: This breaks badly if two features enabled this lookup together. */ 476 unsigned int shift = _hb_ctz (lookup_mask); 477 unsigned int alt_index = ((lookup_mask & glyph_mask) >> shift); 478 479 if (unlikely (alt_index > alt_set.len || alt_index == 0)) return TRACE_RETURN (false); 480 481 glyph_id = alt_set[alt_index - 1]; 482 483 c->replace_glyph (glyph_id); 484 485 return TRACE_RETURN (true); 486 } 487 488 inline bool serialize (hb_serialize_context_t *c, 489 const USHORT *glyphs, 490 unsigned int *alternate_len_list, 491 unsigned int num_glyphs, 492 const USHORT *alternate_glyphs_list) 493 { 494 TRACE_SERIALIZE (); 495 if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false); 496 if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return TRACE_RETURN (false); 497 if (unlikely (!alternateSet.serialize (c, num_glyphs))) return TRACE_RETURN (false); 498 for (unsigned int i = 0; i < num_glyphs; i++) { 499 if (unlikely (!alternateSet[i].serialize (c, this).serialize (c, alternate_glyphs_list, alternate_len_list[i]))) return TRACE_RETURN (false); 500 alternate_glyphs_list += alternate_len_list[i]; 501 } 502 return TRACE_RETURN (true); 503 } 504 505 inline bool sanitize (hb_sanitize_context_t *c) { 506 TRACE_SANITIZE (); 507 return TRACE_RETURN (coverage.sanitize (c, this) && alternateSet.sanitize (c, this)); 508 } 509 510 protected: 511 USHORT format; /* Format identifier--format = 1 */ 512 OffsetTo<Coverage> 513 coverage; /* Offset to Coverage table--from 514 * beginning of Substitution table */ 515 OffsetArrayOf<AlternateSet> 516 alternateSet; /* Array of AlternateSet tables 517 * ordered by Coverage Index */ 518 public: 519 DEFINE_SIZE_ARRAY (6, alternateSet); 520}; 521 522struct AlternateSubst 523{ 524 friend struct SubstLookupSubTable; 525 526 private: 527 528 inline void closure (hb_closure_context_t *c) const 529 { 530 TRACE_CLOSURE (); 531 switch (u.format) { 532 case 1: u.format1.closure (c); break; 533 default: break; 534 } 535 } 536 537 inline const Coverage &get_coverage (void) const 538 { 539 switch (u.format) { 540 case 1: return u.format1.get_coverage (); 541 default:return Null(Coverage); 542 } 543 } 544 545 inline bool apply (hb_apply_context_t *c) const 546 { 547 TRACE_APPLY (); 548 switch (u.format) { 549 case 1: return TRACE_RETURN (u.format1.apply (c)); 550 default:return TRACE_RETURN (false); 551 } 552 } 553 554 inline bool serialize (hb_serialize_context_t *c, 555 const USHORT *glyphs, 556 unsigned int *alternate_len_list, 557 unsigned int num_glyphs, 558 const USHORT *alternate_glyphs_list) 559 { 560 TRACE_SERIALIZE (); 561 if (unlikely (!c->extend_min (u.format))) return TRACE_RETURN (false); 562 unsigned int format = 1; 563 u.format.set (format); 564 switch (u.format) { 565 case 1: return TRACE_RETURN (u.format1.serialize (c, glyphs, alternate_len_list, num_glyphs, alternate_glyphs_list)); 566 default:return TRACE_RETURN (false); 567 } 568 } 569 570 inline bool sanitize (hb_sanitize_context_t *c) { 571 TRACE_SANITIZE (); 572 if (!u.format.sanitize (c)) return TRACE_RETURN (false); 573 switch (u.format) { 574 case 1: return TRACE_RETURN (u.format1.sanitize (c)); 575 default:return TRACE_RETURN (true); 576 } 577 } 578 579 protected: 580 union { 581 USHORT format; /* Format identifier */ 582 AlternateSubstFormat1 format1; 583 } u; 584}; 585 586 587struct Ligature 588{ 589 friend struct LigatureSet; 590 591 private: 592 593 inline void closure (hb_closure_context_t *c) const 594 { 595 TRACE_CLOSURE (); 596 unsigned int count = component.len; 597 for (unsigned int i = 1; i < count; i++) 598 if (!c->glyphs->has (component[i])) 599 return; 600 c->glyphs->add (ligGlyph); 601 } 602 603 inline bool would_apply (hb_would_apply_context_t *c) const 604 { 605 if (c->len != component.len) 606 return false; 607 608 for (unsigned int i = 1; i < c->len; i++) 609 if (likely (c->glyphs[i] != component[i])) 610 return false; 611 612 return true; 613 } 614 615 inline bool apply (hb_apply_context_t *c) const 616 { 617 TRACE_APPLY (); 618 unsigned int count = component.len; 619 if (unlikely (count < 1)) return TRACE_RETURN (false); 620 621 unsigned int end_offset; 622 bool is_mark_ligature; 623 unsigned int total_component_count; 624 625 if (likely (!match_input (c, count, 626 &component[1], 627 match_glyph, 628 NULL, 629 &end_offset, 630 &is_mark_ligature, 631 &total_component_count))) 632 return TRACE_RETURN (false); 633 634 /* Deal, we are forming the ligature. */ 635 c->buffer->merge_clusters (c->buffer->idx, c->buffer->idx + end_offset); 636 637 ligate_input (c, 638 count, 639 &component[1], 640 ligGlyph, 641 match_glyph, 642 NULL, 643 is_mark_ligature, 644 total_component_count); 645 646 return TRACE_RETURN (true); 647 } 648 649 public: 650 inline bool sanitize (hb_sanitize_context_t *c) { 651 TRACE_SANITIZE (); 652 return TRACE_RETURN (ligGlyph.sanitize (c) && component.sanitize (c)); 653 } 654 655 protected: 656 GlyphID ligGlyph; /* GlyphID of ligature to substitute */ 657 HeadlessArrayOf<GlyphID> 658 component; /* Array of component GlyphIDs--start 659 * with the second component--ordered 660 * in writing direction */ 661 public: 662 DEFINE_SIZE_ARRAY (4, component); 663}; 664 665struct LigatureSet 666{ 667 friend struct LigatureSubstFormat1; 668 669 private: 670 671 inline void closure (hb_closure_context_t *c) const 672 { 673 TRACE_CLOSURE (); 674 unsigned int num_ligs = ligature.len; 675 for (unsigned int i = 0; i < num_ligs; i++) 676 (this+ligature[i]).closure (c); 677 } 678 679 inline bool would_apply (hb_would_apply_context_t *c) const 680 { 681 unsigned int num_ligs = ligature.len; 682 for (unsigned int i = 0; i < num_ligs; i++) 683 { 684 const Ligature &lig = this+ligature[i]; 685 if (lig.would_apply (c)) 686 return true; 687 } 688 return false; 689 } 690 691 inline bool apply (hb_apply_context_t *c) const 692 { 693 TRACE_APPLY (); 694 unsigned int num_ligs = ligature.len; 695 for (unsigned int i = 0; i < num_ligs; i++) 696 { 697 const Ligature &lig = this+ligature[i]; 698 if (lig.apply (c)) return TRACE_RETURN (true); 699 } 700 701 return TRACE_RETURN (false); 702 } 703 704 public: 705 inline bool sanitize (hb_sanitize_context_t *c) { 706 TRACE_SANITIZE (); 707 return TRACE_RETURN (ligature.sanitize (c, this)); 708 } 709 710 protected: 711 OffsetArrayOf<Ligature> 712 ligature; /* Array LigatureSet tables 713 * ordered by preference */ 714 public: 715 DEFINE_SIZE_ARRAY (2, ligature); 716}; 717 718struct LigatureSubstFormat1 719{ 720 friend struct LigatureSubst; 721 722 private: 723 724 inline void closure (hb_closure_context_t *c) const 725 { 726 TRACE_CLOSURE (); 727 Coverage::Iter iter; 728 for (iter.init (this+coverage); iter.more (); iter.next ()) { 729 if (c->glyphs->has (iter.get_glyph ())) 730 (this+ligatureSet[iter.get_coverage ()]).closure (c); 731 } 732 } 733 734 inline const Coverage &get_coverage (void) const 735 { 736 return this+coverage; 737 } 738 739 inline bool would_apply (hb_would_apply_context_t *c) const 740 { 741 return (this+ligatureSet[(this+coverage) (c->glyphs[0])]).would_apply (c); 742 } 743 744 inline bool apply (hb_apply_context_t *c) const 745 { 746 TRACE_APPLY (); 747 hb_codepoint_t glyph_id = c->buffer->cur().codepoint; 748 749 unsigned int index = (this+coverage) (glyph_id); 750 if (likely (index == NOT_COVERED)) return TRACE_RETURN (false); 751 752 const LigatureSet &lig_set = this+ligatureSet[index]; 753 return TRACE_RETURN (lig_set.apply (c)); 754 } 755 756 inline bool sanitize (hb_sanitize_context_t *c) { 757 TRACE_SANITIZE (); 758 return TRACE_RETURN (coverage.sanitize (c, this) && ligatureSet.sanitize (c, this)); 759 } 760 761 protected: 762 USHORT format; /* Format identifier--format = 1 */ 763 OffsetTo<Coverage> 764 coverage; /* Offset to Coverage table--from 765 * beginning of Substitution table */ 766 OffsetArrayOf<LigatureSet> 767 ligatureSet; /* Array LigatureSet tables 768 * ordered by Coverage Index */ 769 public: 770 DEFINE_SIZE_ARRAY (6, ligatureSet); 771}; 772 773struct LigatureSubst 774{ 775 friend struct SubstLookupSubTable; 776 777 private: 778 779 inline void closure (hb_closure_context_t *c) const 780 { 781 TRACE_CLOSURE (); 782 switch (u.format) { 783 case 1: u.format1.closure (c); break; 784 default: break; 785 } 786 } 787 788 inline const Coverage &get_coverage (void) const 789 { 790 switch (u.format) { 791 case 1: return u.format1.get_coverage (); 792 default:return Null(Coverage); 793 } 794 } 795 796 inline bool would_apply (hb_would_apply_context_t *c) const 797 { 798 switch (u.format) { 799 case 1: return u.format1.would_apply (c); 800 default:return false; 801 } 802 } 803 804 inline bool apply (hb_apply_context_t *c) const 805 { 806 TRACE_APPLY (); 807 switch (u.format) { 808 case 1: return TRACE_RETURN (u.format1.apply (c)); 809 default:return TRACE_RETURN (false); 810 } 811 } 812 813 inline bool sanitize (hb_sanitize_context_t *c) { 814 TRACE_SANITIZE (); 815 if (!u.format.sanitize (c)) return TRACE_RETURN (false); 816 switch (u.format) { 817 case 1: return TRACE_RETURN (u.format1.sanitize (c)); 818 default:return TRACE_RETURN (true); 819 } 820 } 821 822 protected: 823 union { 824 USHORT format; /* Format identifier */ 825 LigatureSubstFormat1 format1; 826 } u; 827}; 828 829 830static inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index); 831static inline void closure_lookup (hb_closure_context_t *c, unsigned int lookup_index); 832 833struct ContextSubst : Context 834{ 835 friend struct SubstLookupSubTable; 836 837 private: 838 839 inline void closure (hb_closure_context_t *c) const 840 { 841 TRACE_CLOSURE (); 842 return Context::closure (c, closure_lookup); 843 } 844 845 inline bool apply (hb_apply_context_t *c) const 846 { 847 TRACE_APPLY (); 848 return TRACE_RETURN (Context::apply (c, substitute_lookup)); 849 } 850}; 851 852struct ChainContextSubst : ChainContext 853{ 854 friend struct SubstLookupSubTable; 855 856 private: 857 858 inline void closure (hb_closure_context_t *c) const 859 { 860 TRACE_CLOSURE (); 861 return ChainContext::closure (c, closure_lookup); 862 } 863 864 inline bool apply (hb_apply_context_t *c) const 865 { 866 TRACE_APPLY (); 867 return TRACE_RETURN (ChainContext::apply (c, substitute_lookup)); 868 } 869}; 870 871 872struct ExtensionSubst : Extension 873{ 874 friend struct SubstLookupSubTable; 875 friend struct SubstLookup; 876 877 private: 878 inline const struct SubstLookupSubTable& get_subtable (void) const 879 { 880 unsigned int offset = get_offset (); 881 if (unlikely (!offset)) return Null(SubstLookupSubTable); 882 return StructAtOffset<SubstLookupSubTable> (this, offset); 883 } 884 885 inline void closure (hb_closure_context_t *c) const; 886 887 inline const Coverage &get_coverage (void) const; 888 889 inline bool would_apply (hb_would_apply_context_t *c) const; 890 891 inline bool apply (hb_apply_context_t *c) const; 892 893 inline bool sanitize (hb_sanitize_context_t *c); 894 895 inline bool is_reverse (void) const; 896}; 897 898 899struct ReverseChainSingleSubstFormat1 900{ 901 friend struct ReverseChainSingleSubst; 902 903 private: 904 905 inline void closure (hb_closure_context_t *c) const 906 { 907 TRACE_CLOSURE (); 908 const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack); 909 910 unsigned int count; 911 912 count = backtrack.len; 913 for (unsigned int i = 0; i < count; i++) 914 if (!(this+backtrack[i]).intersects (c->glyphs)) 915 return; 916 917 count = lookahead.len; 918 for (unsigned int i = 0; i < count; i++) 919 if (!(this+lookahead[i]).intersects (c->glyphs)) 920 return; 921 922 const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead); 923 Coverage::Iter iter; 924 for (iter.init (this+coverage); iter.more (); iter.next ()) { 925 if (c->glyphs->has (iter.get_glyph ())) 926 c->glyphs->add (substitute[iter.get_coverage ()]); 927 } 928 } 929 930 inline const Coverage &get_coverage (void) const 931 { 932 return this+coverage; 933 } 934 935 inline bool apply (hb_apply_context_t *c) const 936 { 937 TRACE_APPLY (); 938 if (unlikely (c->nesting_level_left != MAX_NESTING_LEVEL)) 939 return TRACE_RETURN (false); /* No chaining to this type */ 940 941 unsigned int index = (this+coverage) (c->buffer->cur().codepoint); 942 if (likely (index == NOT_COVERED)) return TRACE_RETURN (false); 943 944 const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack); 945 const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead); 946 947 if (match_backtrack (c, 948 backtrack.len, (USHORT *) backtrack.array, 949 match_coverage, this) && 950 match_lookahead (c, 951 lookahead.len, (USHORT *) lookahead.array, 952 match_coverage, this, 953 1)) 954 { 955 c->replace_glyph_inplace (substitute[index]); 956 c->buffer->idx--; /* Reverse! */ 957 return TRACE_RETURN (true); 958 } 959 960 return TRACE_RETURN (false); 961 } 962 963 inline bool sanitize (hb_sanitize_context_t *c) { 964 TRACE_SANITIZE (); 965 if (!(coverage.sanitize (c, this) && backtrack.sanitize (c, this))) 966 return TRACE_RETURN (false); 967 OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack); 968 if (!lookahead.sanitize (c, this)) 969 return TRACE_RETURN (false); 970 ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead); 971 return TRACE_RETURN (substitute.sanitize (c)); 972 } 973 974 protected: 975 USHORT format; /* Format identifier--format = 1 */ 976 OffsetTo<Coverage> 977 coverage; /* Offset to Coverage table--from 978 * beginning of table */ 979 OffsetArrayOf<Coverage> 980 backtrack; /* Array of coverage tables 981 * in backtracking sequence, in glyph 982 * sequence order */ 983 OffsetArrayOf<Coverage> 984 lookaheadX; /* Array of coverage tables 985 * in lookahead sequence, in glyph 986 * sequence order */ 987 ArrayOf<GlyphID> 988 substituteX; /* Array of substitute 989 * GlyphIDs--ordered by Coverage Index */ 990 public: 991 DEFINE_SIZE_MIN (10); 992}; 993 994struct ReverseChainSingleSubst 995{ 996 friend struct SubstLookupSubTable; 997 998 private: 999 1000 inline void closure (hb_closure_context_t *c) const 1001 { 1002 TRACE_CLOSURE (); 1003 switch (u.format) { 1004 case 1: u.format1.closure (c); break; 1005 default: break; 1006 } 1007 } 1008 1009 inline const Coverage &get_coverage (void) const 1010 { 1011 switch (u.format) { 1012 case 1: return u.format1.get_coverage (); 1013 default:return Null(Coverage); 1014 } 1015 } 1016 1017 inline bool apply (hb_apply_context_t *c) const 1018 { 1019 TRACE_APPLY (); 1020 switch (u.format) { 1021 case 1: return TRACE_RETURN (u.format1.apply (c)); 1022 default:return TRACE_RETURN (false); 1023 } 1024 } 1025 1026 inline bool sanitize (hb_sanitize_context_t *c) { 1027 TRACE_SANITIZE (); 1028 if (!u.format.sanitize (c)) return TRACE_RETURN (false); 1029 switch (u.format) { 1030 case 1: return TRACE_RETURN (u.format1.sanitize (c)); 1031 default:return TRACE_RETURN (true); 1032 } 1033 } 1034 1035 protected: 1036 union { 1037 USHORT format; /* Format identifier */ 1038 ReverseChainSingleSubstFormat1 format1; 1039 } u; 1040}; 1041 1042 1043 1044/* 1045 * SubstLookup 1046 */ 1047 1048struct SubstLookupSubTable 1049{ 1050 friend struct SubstLookup; 1051 1052 enum Type { 1053 Single = 1, 1054 Multiple = 2, 1055 Alternate = 3, 1056 Ligature = 4, 1057 Context = 5, 1058 ChainContext = 6, 1059 Extension = 7, 1060 ReverseChainSingle = 8 1061 }; 1062 1063 inline void closure (hb_closure_context_t *c, 1064 unsigned int lookup_type) const 1065 { 1066 TRACE_CLOSURE (); 1067 switch (lookup_type) { 1068 case Single: u.single.closure (c); break; 1069 case Multiple: u.multiple.closure (c); break; 1070 case Alternate: u.alternate.closure (c); break; 1071 case Ligature: u.ligature.closure (c); break; 1072 case Context: u.context.closure (c); break; 1073 case ChainContext: u.chainContext.closure (c); break; 1074 case Extension: u.extension.closure (c); break; 1075 case ReverseChainSingle: u.reverseChainContextSingle.closure (c); break; 1076 default: break; 1077 } 1078 } 1079 1080 inline const Coverage &get_coverage (unsigned int lookup_type) const 1081 { 1082 switch (lookup_type) { 1083 case Single: return u.single.get_coverage (); 1084 case Multiple: return u.multiple.get_coverage (); 1085 case Alternate: return u.alternate.get_coverage (); 1086 case Ligature: return u.ligature.get_coverage (); 1087 case Context: return u.context.get_coverage (); 1088 case ChainContext: return u.chainContext.get_coverage (); 1089 case Extension: return u.extension.get_coverage (); 1090 case ReverseChainSingle: return u.reverseChainContextSingle.get_coverage (); 1091 default: return Null(Coverage); 1092 } 1093 } 1094 1095 inline bool would_apply (hb_would_apply_context_t *c, 1096 unsigned int lookup_type) const 1097 { 1098 TRACE_WOULD_APPLY (); 1099 if (get_coverage (lookup_type).get_coverage (c->glyphs[0]) == NOT_COVERED) return false; 1100 if (c->len == 1) { 1101 switch (lookup_type) { 1102 case Single: 1103 case Multiple: 1104 case Alternate: 1105 case ReverseChainSingle: 1106 return true; 1107 } 1108 } 1109 1110 /* Only need to look further for lookups that support substitutions 1111 * of input longer than 1. */ 1112 switch (lookup_type) { 1113 case Ligature: return u.ligature.would_apply (c); 1114 case Context: return u.context.would_apply (c); 1115 case ChainContext: return u.chainContext.would_apply (c); 1116 case Extension: return u.extension.would_apply (c); 1117 default: return false; 1118 } 1119 } 1120 1121 inline bool apply (hb_apply_context_t *c, unsigned int lookup_type) const 1122 { 1123 TRACE_APPLY (); 1124 switch (lookup_type) { 1125 case Single: return TRACE_RETURN (u.single.apply (c)); 1126 case Multiple: return TRACE_RETURN (u.multiple.apply (c)); 1127 case Alternate: return TRACE_RETURN (u.alternate.apply (c)); 1128 case Ligature: return TRACE_RETURN (u.ligature.apply (c)); 1129 case Context: return TRACE_RETURN (u.context.apply (c)); 1130 case ChainContext: return TRACE_RETURN (u.chainContext.apply (c)); 1131 case Extension: return TRACE_RETURN (u.extension.apply (c)); 1132 case ReverseChainSingle: return TRACE_RETURN (u.reverseChainContextSingle.apply (c)); 1133 default: return TRACE_RETURN (false); 1134 } 1135 } 1136 1137 inline bool sanitize (hb_sanitize_context_t *c, unsigned int lookup_type) { 1138 TRACE_SANITIZE (); 1139 if (!u.header.sub_format.sanitize (c)) 1140 return TRACE_RETURN (false); 1141 switch (lookup_type) { 1142 case Single: return TRACE_RETURN (u.single.sanitize (c)); 1143 case Multiple: return TRACE_RETURN (u.multiple.sanitize (c)); 1144 case Alternate: return TRACE_RETURN (u.alternate.sanitize (c)); 1145 case Ligature: return TRACE_RETURN (u.ligature.sanitize (c)); 1146 case Context: return TRACE_RETURN (u.context.sanitize (c)); 1147 case ChainContext: return TRACE_RETURN (u.chainContext.sanitize (c)); 1148 case Extension: return TRACE_RETURN (u.extension.sanitize (c)); 1149 case ReverseChainSingle: return TRACE_RETURN (u.reverseChainContextSingle.sanitize (c)); 1150 default: return TRACE_RETURN (true); 1151 } 1152 } 1153 1154 protected: 1155 union { 1156 struct { 1157 USHORT sub_format; 1158 } header; 1159 SingleSubst single; 1160 MultipleSubst multiple; 1161 AlternateSubst alternate; 1162 LigatureSubst ligature; 1163 ContextSubst context; 1164 ChainContextSubst chainContext; 1165 ExtensionSubst extension; 1166 ReverseChainSingleSubst reverseChainContextSingle; 1167 } u; 1168 public: 1169 DEFINE_SIZE_UNION (2, header.sub_format); 1170}; 1171 1172 1173struct SubstLookup : Lookup 1174{ 1175 inline const SubstLookupSubTable& get_subtable (unsigned int i) const 1176 { return this+CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable)[i]; } 1177 1178 inline static bool lookup_type_is_reverse (unsigned int lookup_type) 1179 { return lookup_type == SubstLookupSubTable::ReverseChainSingle; } 1180 1181 inline bool is_reverse (void) const 1182 { 1183 unsigned int type = get_type (); 1184 if (unlikely (type == SubstLookupSubTable::Extension)) 1185 return CastR<ExtensionSubst> (get_subtable(0)).is_reverse (); 1186 return lookup_type_is_reverse (type); 1187 } 1188 1189 inline void closure (hb_closure_context_t *c) const 1190 { 1191 unsigned int lookup_type = get_type (); 1192 unsigned int count = get_subtable_count (); 1193 for (unsigned int i = 0; i < count; i++) 1194 get_subtable (i).closure (c, lookup_type); 1195 } 1196 1197 template <typename set_t> 1198 inline void add_coverage (set_t *glyphs) const 1199 { 1200 const Coverage *last = NULL; 1201 unsigned int count = get_subtable_count (); 1202 for (unsigned int i = 0; i < count; i++) { 1203 const Coverage *c = &get_subtable (i).get_coverage (get_type ()); 1204 if (c != last) { 1205 c->add_coverage (glyphs); 1206 last = c; 1207 } 1208 } 1209 } 1210 1211 inline bool would_apply (hb_would_apply_context_t *c) const 1212 { 1213 if (unlikely (!c->len)) return false; 1214 if (!c->digest.may_have (c->glyphs[0])) return false; 1215 unsigned int lookup_type = get_type (); 1216 unsigned int count = get_subtable_count (); 1217 for (unsigned int i = 0; i < count; i++) 1218 if (get_subtable (i).would_apply (c, lookup_type)) 1219 return true; 1220 return false; 1221 } 1222 1223 inline bool apply_once (hb_apply_context_t *c) const 1224 { 1225 unsigned int lookup_type = get_type (); 1226 1227 if (!c->check_glyph_property (&c->buffer->cur(), c->lookup_props, &c->property)) 1228 return false; 1229 1230 unsigned int count = get_subtable_count (); 1231 for (unsigned int i = 0; i < count; i++) 1232 if (get_subtable (i).apply (c, lookup_type)) 1233 return true; 1234 1235 return false; 1236 } 1237 1238 inline bool apply_string (hb_apply_context_t *c) const 1239 { 1240 bool ret = false; 1241 1242 if (unlikely (!c->buffer->len)) 1243 return false; 1244 1245 c->set_lookup (*this); 1246 1247 if (likely (!is_reverse ())) 1248 { 1249 /* in/out forward substitution */ 1250 c->buffer->clear_output (); 1251 c->buffer->idx = 0; 1252 1253 while (c->buffer->idx < c->buffer->len) 1254 { 1255 if ((c->buffer->cur().mask & c->lookup_mask) && 1256 c->digest.may_have (c->buffer->cur().codepoint) && 1257 apply_once (c)) 1258 ret = true; 1259 else 1260 c->buffer->next_glyph (); 1261 } 1262 if (ret) 1263 c->buffer->swap_buffers (); 1264 } 1265 else 1266 { 1267 /* in-place backward substitution */ 1268 c->buffer->idx = c->buffer->len - 1; 1269 do 1270 { 1271 if ((c->buffer->cur().mask & c->lookup_mask) && 1272 c->digest.may_have (c->buffer->cur().codepoint) && 1273 apply_once (c)) 1274 ret = true; 1275 else 1276 c->buffer->idx--; 1277 1278 } 1279 while ((int) c->buffer->idx >= 0); 1280 } 1281 1282 return ret; 1283 } 1284 1285 inline bool sanitize (hb_sanitize_context_t *c) { 1286 TRACE_SANITIZE (); 1287 if (unlikely (!Lookup::sanitize (c))) return TRACE_RETURN (false); 1288 OffsetArrayOf<SubstLookupSubTable> &list = CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable); 1289 if (unlikely (!list.sanitize (c, this, get_type ()))) return TRACE_RETURN (false); 1290 1291 if (unlikely (get_type () == SubstLookupSubTable::Extension)) 1292 { 1293 /* The spec says all subtables of an Extension lookup should 1294 * have the same type. This is specially important if one has 1295 * a reverse type! 1296 * 1297 * We just check that they are all either forward, or reverse. */ 1298 unsigned int type = get_subtable (0).u.extension.get_type (); 1299 unsigned int count = get_subtable_count (); 1300 for (unsigned int i = 1; i < count; i++) 1301 if (get_subtable (i).u.extension.get_type () != type) 1302 return TRACE_RETURN (false); 1303 } 1304 return TRACE_RETURN (true); 1305 } 1306}; 1307 1308typedef OffsetListOf<SubstLookup> SubstLookupList; 1309 1310/* 1311 * GSUB -- The Glyph Substitution Table 1312 */ 1313 1314struct GSUB : GSUBGPOS 1315{ 1316 static const hb_tag_t Tag = HB_OT_TAG_GSUB; 1317 1318 inline const SubstLookup& get_lookup (unsigned int i) const 1319 { return CastR<SubstLookup> (GSUBGPOS::get_lookup (i)); } 1320 1321 template <typename set_t> 1322 inline void add_coverage (set_t *glyphs, unsigned int lookup_index) const 1323 { get_lookup (lookup_index).add_coverage (glyphs); } 1324 1325 inline bool would_substitute_lookup (hb_would_apply_context_t *c, unsigned int lookup_index) const 1326 { return get_lookup (lookup_index).would_apply (c); } 1327 1328 inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index) const 1329 { return get_lookup (lookup_index).apply_string (c); } 1330 1331 static inline void substitute_start (hb_font_t *font, hb_buffer_t *buffer); 1332 static inline void substitute_finish (hb_font_t *font, hb_buffer_t *buffer); 1333 1334 inline void closure_lookup (hb_closure_context_t *c, 1335 unsigned int lookup_index) const 1336 { return get_lookup (lookup_index).closure (c); } 1337 1338 inline bool sanitize (hb_sanitize_context_t *c) { 1339 TRACE_SANITIZE (); 1340 if (unlikely (!GSUBGPOS::sanitize (c))) return TRACE_RETURN (false); 1341 OffsetTo<SubstLookupList> &list = CastR<OffsetTo<SubstLookupList> > (lookupList); 1342 return TRACE_RETURN (list.sanitize (c, this)); 1343 } 1344 public: 1345 DEFINE_SIZE_STATIC (10); 1346}; 1347 1348 1349void 1350GSUB::substitute_start (hb_font_t *font, hb_buffer_t *buffer) 1351{ 1352 HB_BUFFER_ALLOCATE_VAR (buffer, glyph_props); 1353 HB_BUFFER_ALLOCATE_VAR (buffer, lig_props); 1354 HB_BUFFER_ALLOCATE_VAR (buffer, syllable); 1355 1356 const GDEF &gdef = *hb_ot_layout_from_face (font->face)->gdef; 1357 unsigned int count = buffer->len; 1358 for (unsigned int i = 0; i < count; i++) { 1359 buffer->info[i].lig_props() = buffer->info[i].syllable() = 0; 1360 buffer->info[i].glyph_props() = gdef.get_glyph_props (buffer->info[i].codepoint); 1361 } 1362} 1363 1364void 1365GSUB::substitute_finish (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer HB_UNUSED) 1366{ 1367} 1368 1369 1370/* Out-of-class implementation for methods recursing */ 1371 1372inline void ExtensionSubst::closure (hb_closure_context_t *c) const 1373{ 1374 get_subtable ().closure (c, get_type ()); 1375} 1376 1377inline const Coverage & ExtensionSubst::get_coverage (void) const 1378{ 1379 return get_subtable ().get_coverage (get_type ()); 1380} 1381 1382inline bool ExtensionSubst::would_apply (hb_would_apply_context_t *c) const 1383{ 1384 return get_subtable ().would_apply (c, get_type ()); 1385} 1386 1387inline bool ExtensionSubst::apply (hb_apply_context_t *c) const 1388{ 1389 TRACE_APPLY (); 1390 return TRACE_RETURN (get_subtable ().apply (c, get_type ())); 1391} 1392 1393inline bool ExtensionSubst::sanitize (hb_sanitize_context_t *c) 1394{ 1395 TRACE_SANITIZE (); 1396 if (unlikely (!Extension::sanitize (c))) return TRACE_RETURN (false); 1397 unsigned int offset = get_offset (); 1398 if (unlikely (!offset)) return TRACE_RETURN (true); 1399 return TRACE_RETURN (StructAtOffset<SubstLookupSubTable> (this, offset).sanitize (c, get_type ())); 1400} 1401 1402inline bool ExtensionSubst::is_reverse (void) const 1403{ 1404 unsigned int type = get_type (); 1405 if (unlikely (type == SubstLookupSubTable::Extension)) 1406 return CastR<ExtensionSubst> (get_subtable()).is_reverse (); 1407 return SubstLookup::lookup_type_is_reverse (type); 1408} 1409 1410static inline void closure_lookup (hb_closure_context_t *c, unsigned int lookup_index) 1411{ 1412 const GSUB &gsub = *(hb_ot_layout_from_face (c->face)->gsub); 1413 const SubstLookup &l = gsub.get_lookup (lookup_index); 1414 1415 if (unlikely (c->nesting_level_left == 0)) 1416 return; 1417 1418 c->nesting_level_left--; 1419 l.closure (c); 1420 c->nesting_level_left++; 1421} 1422 1423static inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index) 1424{ 1425 const GSUB &gsub = *(hb_ot_layout_from_face (c->face)->gsub); 1426 const SubstLookup &l = gsub.get_lookup (lookup_index); 1427 1428 if (unlikely (c->nesting_level_left == 0)) 1429 return false; 1430 1431 hb_apply_context_t new_c (*c); 1432 new_c.nesting_level_left--; 1433 new_c.set_lookup (l); 1434 return l.apply_once (&new_c); 1435} 1436 1437 1438} // namespace OT 1439 1440 1441#endif /* HB_OT_LAYOUT_GSUB_TABLE_HH */ 1442