hb-ot-layout-gsub-table.hh revision 4d3aeb8cb2bc1ca7cdd03ba28ba8c334f12d4c03
1/* 2 * Copyright © 2007,2008,2009,2010 Red Hat, Inc. 3 * Copyright © 2010 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 bool apply (hb_apply_context_t *c) const 43 { 44 TRACE_APPLY (); 45 hb_codepoint_t glyph_id = c->buffer->info[c->buffer->idx].codepoint; 46 unsigned int index = (this+coverage) (glyph_id); 47 if (likely (index == NOT_COVERED)) 48 return false; 49 50 /* According to the Adobe Annotated OpenType Suite, result is always 51 * limited to 16bit. */ 52 glyph_id = (glyph_id + deltaGlyphID) & 0xFFFF; 53 c->replace_glyph (glyph_id); 54 55 return true; 56 } 57 58 inline bool sanitize (hb_sanitize_context_t *c) { 59 TRACE_SANITIZE (); 60 return coverage.sanitize (c, this) 61 && deltaGlyphID.sanitize (c); 62 } 63 64 private: 65 USHORT format; /* Format identifier--format = 1 */ 66 OffsetTo<Coverage> 67 coverage; /* Offset to Coverage table--from 68 * beginning of Substitution table */ 69 SHORT deltaGlyphID; /* Add to original GlyphID to get 70 * substitute GlyphID */ 71 public: 72 DEFINE_SIZE_STATIC (6); 73}; 74 75struct SingleSubstFormat2 76{ 77 friend struct SingleSubst; 78 79 private: 80 81 inline bool apply (hb_apply_context_t *c) const 82 { 83 TRACE_APPLY (); 84 hb_codepoint_t glyph_id = c->buffer->info[c->buffer->idx].codepoint; 85 unsigned int index = (this+coverage) (glyph_id); 86 if (likely (index == NOT_COVERED)) 87 return false; 88 89 if (unlikely (index >= substitute.len)) 90 return false; 91 92 glyph_id = substitute[index]; 93 c->replace_glyph (glyph_id); 94 95 return true; 96 } 97 98 inline bool sanitize (hb_sanitize_context_t *c) { 99 TRACE_SANITIZE (); 100 return coverage.sanitize (c, this) 101 && substitute.sanitize (c); 102 } 103 104 private: 105 USHORT format; /* Format identifier--format = 2 */ 106 OffsetTo<Coverage> 107 coverage; /* Offset to Coverage table--from 108 * beginning of Substitution table */ 109 ArrayOf<GlyphID> 110 substitute; /* Array of substitute 111 * GlyphIDs--ordered by Coverage Index */ 112 public: 113 DEFINE_SIZE_ARRAY (6, substitute); 114}; 115 116struct SingleSubst 117{ 118 friend struct SubstLookupSubTable; 119 120 private: 121 122 inline bool apply (hb_apply_context_t *c) const 123 { 124 TRACE_APPLY (); 125 switch (u.format) { 126 case 1: return u.format1.apply (c); 127 case 2: return u.format2.apply (c); 128 default:return false; 129 } 130 } 131 132 inline bool sanitize (hb_sanitize_context_t *c) { 133 TRACE_SANITIZE (); 134 if (!u.format.sanitize (c)) return false; 135 switch (u.format) { 136 case 1: return u.format1.sanitize (c); 137 case 2: return u.format2.sanitize (c); 138 default:return true; 139 } 140 } 141 142 private: 143 union { 144 USHORT format; /* Format identifier */ 145 SingleSubstFormat1 format1; 146 SingleSubstFormat2 format2; 147 } u; 148}; 149 150 151struct Sequence 152{ 153 friend struct MultipleSubstFormat1; 154 155 private: 156 inline bool apply (hb_apply_context_t *c) const 157 { 158 TRACE_APPLY (); 159 if (unlikely (!substitute.len)) 160 return false; 161 162 if (c->property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE) 163 c->guess_glyph_class (HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH); 164 c->replace_glyphs_be16 (1, substitute.len, (const uint16_t *) substitute.array); 165 166 return true; 167 } 168 169 public: 170 inline bool sanitize (hb_sanitize_context_t *c) { 171 TRACE_SANITIZE (); 172 return substitute.sanitize (c); 173 } 174 175 private: 176 ArrayOf<GlyphID> 177 substitute; /* String of GlyphIDs to substitute */ 178 public: 179 DEFINE_SIZE_ARRAY (2, substitute); 180}; 181 182struct MultipleSubstFormat1 183{ 184 friend struct MultipleSubst; 185 186 private: 187 188 inline bool apply (hb_apply_context_t *c) const 189 { 190 TRACE_APPLY (); 191 192 unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint); 193 if (likely (index == NOT_COVERED)) 194 return false; 195 196 return (this+sequence[index]).apply (c); 197 } 198 199 inline bool sanitize (hb_sanitize_context_t *c) { 200 TRACE_SANITIZE (); 201 return coverage.sanitize (c, this) 202 && sequence.sanitize (c, this); 203 } 204 205 private: 206 USHORT format; /* Format identifier--format = 1 */ 207 OffsetTo<Coverage> 208 coverage; /* Offset to Coverage table--from 209 * beginning of Substitution table */ 210 OffsetArrayOf<Sequence> 211 sequence; /* Array of Sequence tables 212 * ordered by Coverage Index */ 213 public: 214 DEFINE_SIZE_ARRAY (6, sequence); 215}; 216 217struct MultipleSubst 218{ 219 friend struct SubstLookupSubTable; 220 221 private: 222 223 inline bool apply (hb_apply_context_t *c) const 224 { 225 TRACE_APPLY (); 226 switch (u.format) { 227 case 1: return u.format1.apply (c); 228 default:return false; 229 } 230 } 231 232 inline bool sanitize (hb_sanitize_context_t *c) { 233 TRACE_SANITIZE (); 234 if (!u.format.sanitize (c)) return false; 235 switch (u.format) { 236 case 1: return u.format1.sanitize (c); 237 default:return true; 238 } 239 } 240 241 private: 242 union { 243 USHORT format; /* Format identifier */ 244 MultipleSubstFormat1 format1; 245 } u; 246}; 247 248 249typedef ArrayOf<GlyphID> AlternateSet; /* Array of alternate GlyphIDs--in 250 * arbitrary order */ 251 252struct AlternateSubstFormat1 253{ 254 friend struct AlternateSubst; 255 256 private: 257 258 inline bool apply (hb_apply_context_t *c) const 259 { 260 TRACE_APPLY (); 261 hb_codepoint_t glyph_id = c->buffer->info[c->buffer->idx].codepoint; 262 hb_mask_t glyph_mask = c->buffer->info[c->buffer->idx].mask; 263 hb_mask_t lookup_mask = c->lookup_mask; 264 265 unsigned int index = (this+coverage) (glyph_id); 266 if (likely (index == NOT_COVERED)) 267 return false; 268 269 const AlternateSet &alt_set = this+alternateSet[index]; 270 271 if (unlikely (!alt_set.len)) 272 return false; 273 274 /* Note: This breaks badly if two features enabled this lookup together. */ 275 unsigned int shift = _hb_ctz (lookup_mask); 276 unsigned int alt_index = ((lookup_mask & glyph_mask) >> shift); 277 278 if (unlikely (alt_index > alt_set.len || alt_index == 0)) 279 return false; 280 281 glyph_id = alt_set[alt_index - 1]; 282 283 c->replace_glyph (glyph_id); 284 285 return true; 286 } 287 288 inline bool sanitize (hb_sanitize_context_t *c) { 289 TRACE_SANITIZE (); 290 return coverage.sanitize (c, this) 291 && alternateSet.sanitize (c, this); 292 } 293 294 private: 295 USHORT format; /* Format identifier--format = 1 */ 296 OffsetTo<Coverage> 297 coverage; /* Offset to Coverage table--from 298 * beginning of Substitution table */ 299 OffsetArrayOf<AlternateSet> 300 alternateSet; /* Array of AlternateSet tables 301 * ordered by Coverage Index */ 302 public: 303 DEFINE_SIZE_ARRAY (6, alternateSet); 304}; 305 306struct AlternateSubst 307{ 308 friend struct SubstLookupSubTable; 309 310 private: 311 312 inline bool apply (hb_apply_context_t *c) const 313 { 314 TRACE_APPLY (); 315 switch (u.format) { 316 case 1: return u.format1.apply (c); 317 default:return false; 318 } 319 } 320 321 inline bool sanitize (hb_sanitize_context_t *c) { 322 TRACE_SANITIZE (); 323 if (!u.format.sanitize (c)) return false; 324 switch (u.format) { 325 case 1: return u.format1.sanitize (c); 326 default:return true; 327 } 328 } 329 330 private: 331 union { 332 USHORT format; /* Format identifier */ 333 AlternateSubstFormat1 format1; 334 } u; 335}; 336 337 338struct Ligature 339{ 340 friend struct LigatureSet; 341 342 private: 343 inline bool apply (hb_apply_context_t *c) const 344 { 345 TRACE_APPLY (); 346 unsigned int i; 347 unsigned int j = c->buffer->idx; 348 unsigned int count = component.len; 349 unsigned int end = MIN (c->buffer->len, j + c->context_length); 350 if (unlikely (count < 2 || j >= end)) 351 return false; 352 353 bool first_was_mark = (c->property & HB_OT_LAYOUT_GLYPH_CLASS_MARK); 354 bool found_non_mark = false; 355 356 for (i = 1; i < count; i++) 357 { 358 unsigned int property; 359 do 360 { 361 j++; 362 if (unlikely (j == end)) 363 return false; 364 } while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[j], c->lookup_props, &property)); 365 366 found_non_mark |= !(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK); 367 368 if (likely (c->buffer->info[j].codepoint != component[i])) 369 return false; 370 } 371 372 if (first_was_mark && found_non_mark) 373 c->guess_glyph_class (HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE); 374 375 /* Allocate new ligature id */ 376 unsigned int lig_id = allocate_lig_id (c->buffer); 377 c->buffer->info[c->buffer->idx].lig_comp() = 0; 378 c->buffer->info[c->buffer->idx].lig_id() = lig_id; 379 380 if (j < c->buffer->idx + count) /* No input glyphs skipped */ 381 { 382 c->replace_glyphs_be16 (count, 1, (const uint16_t *) &ligGlyph); 383 } 384 else 385 { 386 c->replace_glyph (ligGlyph); 387 388 /* Now we must do a second loop to copy the skipped glyphs to 389 `out' and assign component values to it. We start with the 390 glyph after the first component. Glyphs between component 391 i and i+1 belong to component i. Together with the lig_id 392 value it is later possible to check whether a specific 393 component value really belongs to a given ligature. */ 394 395 for (i = 1; i < count; i++) 396 { 397 while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[c->buffer->idx], c->lookup_props, NULL)) 398 { 399 c->buffer->info[c->buffer->idx].lig_comp() = i; 400 c->buffer->info[c->buffer->idx].lig_id() = lig_id; 401 c->replace_glyph (c->buffer->info[c->buffer->idx].codepoint); 402 } 403 404 /* Skip the base glyph */ 405 c->buffer->idx++; 406 } 407 } 408 409 return true; 410 } 411 412 public: 413 inline bool sanitize (hb_sanitize_context_t *c) { 414 TRACE_SANITIZE (); 415 return ligGlyph.sanitize (c) 416 && component.sanitize (c); 417 } 418 419 private: 420 GlyphID ligGlyph; /* GlyphID of ligature to substitute */ 421 HeadlessArrayOf<GlyphID> 422 component; /* Array of component GlyphIDs--start 423 * with the second component--ordered 424 * in writing direction */ 425 public: 426 DEFINE_SIZE_ARRAY (4, component); 427}; 428 429struct LigatureSet 430{ 431 friend struct LigatureSubstFormat1; 432 433 private: 434 inline bool apply (hb_apply_context_t *c) const 435 { 436 TRACE_APPLY (); 437 unsigned int num_ligs = ligature.len; 438 for (unsigned int i = 0; i < num_ligs; i++) 439 { 440 const Ligature &lig = this+ligature[i]; 441 if (lig.apply (c)) 442 return true; 443 } 444 445 return false; 446 } 447 448 public: 449 inline bool sanitize (hb_sanitize_context_t *c) { 450 TRACE_SANITIZE (); 451 return ligature.sanitize (c, this); 452 } 453 454 private: 455 OffsetArrayOf<Ligature> 456 ligature; /* Array LigatureSet tables 457 * ordered by preference */ 458 public: 459 DEFINE_SIZE_ARRAY (2, ligature); 460}; 461 462struct LigatureSubstFormat1 463{ 464 friend struct LigatureSubst; 465 466 private: 467 inline bool apply (hb_apply_context_t *c) const 468 { 469 TRACE_APPLY (); 470 hb_codepoint_t glyph_id = c->buffer->info[c->buffer->idx].codepoint; 471 472 unsigned int index = (this+coverage) (glyph_id); 473 if (likely (index == NOT_COVERED)) 474 return false; 475 476 const LigatureSet &lig_set = this+ligatureSet[index]; 477 return lig_set.apply (c); 478 } 479 480 inline bool sanitize (hb_sanitize_context_t *c) { 481 TRACE_SANITIZE (); 482 return coverage.sanitize (c, this) 483 && ligatureSet.sanitize (c, this); 484 } 485 486 private: 487 USHORT format; /* Format identifier--format = 1 */ 488 OffsetTo<Coverage> 489 coverage; /* Offset to Coverage table--from 490 * beginning of Substitution table */ 491 OffsetArrayOf<LigatureSet> 492 ligatureSet; /* Array LigatureSet tables 493 * ordered by Coverage Index */ 494 public: 495 DEFINE_SIZE_ARRAY (6, ligatureSet); 496}; 497 498struct LigatureSubst 499{ 500 friend struct SubstLookupSubTable; 501 502 private: 503 inline bool apply (hb_apply_context_t *c) const 504 { 505 TRACE_APPLY (); 506 switch (u.format) { 507 case 1: return u.format1.apply (c); 508 default:return false; 509 } 510 } 511 512 inline bool sanitize (hb_sanitize_context_t *c) { 513 TRACE_SANITIZE (); 514 if (!u.format.sanitize (c)) return false; 515 switch (u.format) { 516 case 1: return u.format1.sanitize (c); 517 default:return true; 518 } 519 } 520 521 private: 522 union { 523 USHORT format; /* Format identifier */ 524 LigatureSubstFormat1 format1; 525 } u; 526}; 527 528 529static inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index); 530 531struct ContextSubst : Context 532{ 533 friend struct SubstLookupSubTable; 534 535 private: 536 inline bool apply (hb_apply_context_t *c) const 537 { 538 TRACE_APPLY (); 539 return Context::apply (c, substitute_lookup); 540 } 541}; 542 543struct ChainContextSubst : ChainContext 544{ 545 friend struct SubstLookupSubTable; 546 547 private: 548 inline bool apply (hb_apply_context_t *c) const 549 { 550 TRACE_APPLY (); 551 return ChainContext::apply (c, substitute_lookup); 552 } 553}; 554 555 556struct ExtensionSubst : Extension 557{ 558 friend struct SubstLookupSubTable; 559 friend struct SubstLookup; 560 561 private: 562 inline const struct SubstLookupSubTable& get_subtable (void) const 563 { 564 unsigned int offset = get_offset (); 565 if (unlikely (!offset)) return Null(SubstLookupSubTable); 566 return StructAtOffset<SubstLookupSubTable> (this, offset); 567 } 568 569 inline bool apply (hb_apply_context_t *c) const; 570 571 inline bool sanitize (hb_sanitize_context_t *c); 572 573 inline bool is_reverse (void) const; 574}; 575 576 577struct ReverseChainSingleSubstFormat1 578{ 579 friend struct ReverseChainSingleSubst; 580 581 private: 582 inline bool apply (hb_apply_context_t *c) const 583 { 584 TRACE_APPLY (); 585 if (unlikely (c->context_length != NO_CONTEXT)) 586 return false; /* No chaining to this type */ 587 588 unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint); 589 if (likely (index == NOT_COVERED)) 590 return false; 591 592 const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack); 593 const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead); 594 595 if (match_backtrack (c, 596 backtrack.len, (USHORT *) backtrack.array, 597 match_coverage, this) && 598 match_lookahead (c, 599 lookahead.len, (USHORT *) lookahead.array, 600 match_coverage, this, 601 1)) 602 { 603 c->buffer->info[c->buffer->idx].codepoint = substitute[index]; 604 c->buffer->idx--; /* Reverse! */ 605 return true; 606 } 607 608 return false; 609 } 610 611 inline bool sanitize (hb_sanitize_context_t *c) { 612 TRACE_SANITIZE (); 613 if (!(coverage.sanitize (c, this) 614 && backtrack.sanitize (c, this))) 615 return false; 616 OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack); 617 if (!lookahead.sanitize (c, this)) 618 return false; 619 ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead); 620 return substitute.sanitize (c); 621 } 622 623 private: 624 USHORT format; /* Format identifier--format = 1 */ 625 OffsetTo<Coverage> 626 coverage; /* Offset to Coverage table--from 627 * beginning of table */ 628 OffsetArrayOf<Coverage> 629 backtrack; /* Array of coverage tables 630 * in backtracking sequence, in glyph 631 * sequence order */ 632 OffsetArrayOf<Coverage> 633 lookaheadX; /* Array of coverage tables 634 * in lookahead sequence, in glyph 635 * sequence order */ 636 ArrayOf<GlyphID> 637 substituteX; /* Array of substitute 638 * GlyphIDs--ordered by Coverage Index */ 639 public: 640 DEFINE_SIZE_MIN (10); 641}; 642 643struct ReverseChainSingleSubst 644{ 645 friend struct SubstLookupSubTable; 646 647 private: 648 inline bool apply (hb_apply_context_t *c) const 649 { 650 TRACE_APPLY (); 651 switch (u.format) { 652 case 1: return u.format1.apply (c); 653 default:return false; 654 } 655 } 656 657 inline bool sanitize (hb_sanitize_context_t *c) { 658 TRACE_SANITIZE (); 659 if (!u.format.sanitize (c)) return false; 660 switch (u.format) { 661 case 1: return u.format1.sanitize (c); 662 default:return true; 663 } 664 } 665 666 private: 667 union { 668 USHORT format; /* Format identifier */ 669 ReverseChainSingleSubstFormat1 format1; 670 } u; 671}; 672 673 674 675/* 676 * SubstLookup 677 */ 678 679struct SubstLookupSubTable 680{ 681 friend struct SubstLookup; 682 683 enum { 684 Single = 1, 685 Multiple = 2, 686 Alternate = 3, 687 Ligature = 4, 688 Context = 5, 689 ChainContext = 6, 690 Extension = 7, 691 ReverseChainSingle = 8 692 }; 693 694 inline bool apply (hb_apply_context_t *c, unsigned int lookup_type) const 695 { 696 TRACE_APPLY (); 697 switch (lookup_type) { 698 case Single: return u.single.apply (c); 699 case Multiple: return u.multiple.apply (c); 700 case Alternate: return u.alternate.apply (c); 701 case Ligature: return u.ligature.apply (c); 702 case Context: return u.c.apply (c); 703 case ChainContext: return u.chainContext.apply (c); 704 case Extension: return u.extension.apply (c); 705 case ReverseChainSingle: return u.reverseChainContextSingle.apply (c); 706 default:return false; 707 } 708 } 709 710 inline bool sanitize (hb_sanitize_context_t *c, unsigned int lookup_type) { 711 TRACE_SANITIZE (); 712 switch (lookup_type) { 713 case Single: return u.single.sanitize (c); 714 case Multiple: return u.multiple.sanitize (c); 715 case Alternate: return u.alternate.sanitize (c); 716 case Ligature: return u.ligature.sanitize (c); 717 case Context: return u.c.sanitize (c); 718 case ChainContext: return u.chainContext.sanitize (c); 719 case Extension: return u.extension.sanitize (c); 720 case ReverseChainSingle: return u.reverseChainContextSingle.sanitize (c); 721 default:return true; 722 } 723 } 724 725 private: 726 union { 727 USHORT sub_format; 728 SingleSubst single; 729 MultipleSubst multiple; 730 AlternateSubst alternate; 731 LigatureSubst ligature; 732 ContextSubst c; 733 ChainContextSubst chainContext; 734 ExtensionSubst extension; 735 ReverseChainSingleSubst reverseChainContextSingle; 736 } u; 737 public: 738 DEFINE_SIZE_UNION (2, sub_format); 739}; 740 741 742struct SubstLookup : Lookup 743{ 744 inline const SubstLookupSubTable& get_subtable (unsigned int i) const 745 { return this+CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable)[i]; } 746 747 inline static bool lookup_type_is_reverse (unsigned int lookup_type) 748 { return lookup_type == SubstLookupSubTable::ReverseChainSingle; } 749 750 inline bool is_reverse (void) const 751 { 752 unsigned int type = get_type (); 753 if (unlikely (type == SubstLookupSubTable::Extension)) 754 return CastR<ExtensionSubst> (get_subtable(0)).is_reverse (); 755 return lookup_type_is_reverse (type); 756 } 757 758 759 inline bool apply_once (hb_face_t *face, 760 hb_buffer_t *buffer, 761 hb_mask_t lookup_mask, 762 unsigned int context_length, 763 unsigned int nesting_level_left) const 764 { 765 unsigned int lookup_type = get_type (); 766 hb_apply_context_t c[1] = {{0}}; 767 768 c->face = face; 769 c->buffer = buffer; 770 c->direction = buffer->props.direction; 771 c->lookup_mask = lookup_mask; 772 c->context_length = context_length; 773 c->nesting_level_left = nesting_level_left; 774 c->lookup_props = get_props (); 775 776 if (!_hb_ot_layout_check_glyph_property (c->face, &c->buffer->info[c->buffer->idx], c->lookup_props, &c->property)) 777 return false; 778 779 if (unlikely (lookup_type == SubstLookupSubTable::Extension)) 780 { 781 /* The spec says all subtables should have the same type. 782 * This is specially important if one has a reverse type! 783 * 784 * This is rather slow to do this here for every glyph, 785 * but it's easiest, and who uses extension lookups anyway?!*/ 786 unsigned int count = get_subtable_count (); 787 unsigned int type = get_subtable(0).u.extension.get_type (); 788 for (unsigned int i = 1; i < count; i++) 789 if (get_subtable(i).u.extension.get_type () != type) 790 return false; 791 } 792 793 unsigned int count = get_subtable_count (); 794 for (unsigned int i = 0; i < count; i++) 795 if (get_subtable (i).apply (c, lookup_type)) 796 return true; 797 798 return false; 799 } 800 801 inline bool apply_string (hb_face_t *face, 802 hb_buffer_t *buffer, 803 hb_mask_t mask) const 804 { 805 bool ret = false; 806 807 if (unlikely (!buffer->len)) 808 return false; 809 810 if (likely (!is_reverse ())) 811 { 812 /* in/out forward substitution */ 813 buffer->clear_output (); 814 buffer->idx = 0; 815 while (buffer->idx < buffer->len) 816 { 817 if ((buffer->info[buffer->idx].mask & mask) && 818 apply_once (face, buffer, mask, NO_CONTEXT, MAX_NESTING_LEVEL)) 819 ret = true; 820 else 821 buffer->next_glyph (); 822 823 } 824 if (ret) 825 buffer->swap_buffers (); 826 } 827 else 828 { 829 /* in-place backward substitution */ 830 buffer->idx = buffer->len - 1; 831 do 832 { 833 if ((buffer->info[buffer->idx].mask & mask) && 834 apply_once (face, buffer, mask, NO_CONTEXT, MAX_NESTING_LEVEL)) 835 ret = true; 836 else 837 buffer->idx--; 838 839 } 840 while ((int) buffer->idx >= 0); 841 } 842 843 return ret; 844 } 845 846 inline bool sanitize (hb_sanitize_context_t *c) { 847 TRACE_SANITIZE (); 848 if (unlikely (!Lookup::sanitize (c))) return false; 849 OffsetArrayOf<SubstLookupSubTable> &list = CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable); 850 return list.sanitize (c, this, get_type ()); 851 } 852}; 853 854typedef OffsetListOf<SubstLookup> SubstLookupList; 855 856/* 857 * GSUB -- The Glyph Substitution Table 858 */ 859 860struct GSUB : GSUBGPOS 861{ 862 static const hb_tag_t Tag = HB_OT_TAG_GSUB; 863 864 inline const SubstLookup& get_lookup (unsigned int i) const 865 { return CastR<SubstLookup> (GSUBGPOS::get_lookup (i)); } 866 867 inline bool substitute_lookup (hb_face_t *face, 868 hb_buffer_t *buffer, 869 unsigned int lookup_index, 870 hb_mask_t mask) const 871 { return get_lookup (lookup_index).apply_string (face, buffer, mask); } 872 873 static inline void substitute_start (hb_buffer_t *buffer); 874 static inline void substitute_finish (hb_buffer_t *buffer); 875 876 inline bool sanitize (hb_sanitize_context_t *c) { 877 TRACE_SANITIZE (); 878 if (unlikely (!GSUBGPOS::sanitize (c))) return false; 879 OffsetTo<SubstLookupList> &list = CastR<OffsetTo<SubstLookupList> > (lookupList); 880 return list.sanitize (c, this); 881 } 882 public: 883 DEFINE_SIZE_STATIC (10); 884}; 885 886 887void 888GSUB::substitute_start (hb_buffer_t *buffer) 889{ 890 HB_BUFFER_ALLOCATE_VAR (buffer, props_cache); 891 HB_BUFFER_ALLOCATE_VAR (buffer, lig_id); 892 HB_BUFFER_ALLOCATE_VAR (buffer, lig_comp); 893 894 unsigned int count = buffer->len; 895 for (unsigned int i = 0; i < count; i++) 896 buffer->info[i].props_cache() = buffer->info[i].lig_id() = buffer->info[i].lig_comp() = 0; 897} 898 899void 900GSUB::substitute_finish (hb_buffer_t *buffer) 901{ 902} 903 904 905/* Out-of-class implementation for methods recursing */ 906 907inline bool ExtensionSubst::apply (hb_apply_context_t *c) const 908{ 909 TRACE_APPLY (); 910 return get_subtable ().apply (c, get_type ()); 911} 912 913inline bool ExtensionSubst::sanitize (hb_sanitize_context_t *c) 914{ 915 TRACE_SANITIZE (); 916 if (unlikely (!Extension::sanitize (c))) return false; 917 unsigned int offset = get_offset (); 918 if (unlikely (!offset)) return true; 919 return StructAtOffset<SubstLookupSubTable> (this, offset).sanitize (c, get_type ()); 920} 921 922inline bool ExtensionSubst::is_reverse (void) const 923{ 924 unsigned int type = get_type (); 925 if (unlikely (type == SubstLookupSubTable::Extension)) 926 return CastR<ExtensionSubst> (get_subtable()).is_reverse (); 927 return SubstLookup::lookup_type_is_reverse (type); 928} 929 930static inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index) 931{ 932 const GSUB &gsub = *(c->face->ot_layout->gsub); 933 const SubstLookup &l = gsub.get_lookup (lookup_index); 934 935 if (unlikely (c->nesting_level_left == 0)) 936 return false; 937 938 if (unlikely (c->context_length < 1)) 939 return false; 940 941 return l.apply_once (c->face, c->buffer, c->lookup_mask, c->context_length, c->nesting_level_left - 1); 942} 943 944 945 946#endif /* HB_OT_LAYOUT_GSUB_TABLE_HH */ 947