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