hb-ot-layout-gsub-table.hh revision 370f03e9c69d98d735eafb7e72b13b17f42cbaa9
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 j = c->buffer->idx; 347 unsigned int count = component.len; 348 unsigned int end = MIN (c->buffer->len, j + c->context_length); 349 if (unlikely (count < 2 || j >= end)) 350 return false; 351 352 bool first_was_mark = (c->property & HB_OT_LAYOUT_GLYPH_CLASS_MARK); 353 bool found_non_mark = false; 354 355 for (unsigned int i = 1; i < count; i++) 356 { 357 unsigned int property; 358 do 359 { 360 j++; 361 if (unlikely (j == end)) 362 return false; 363 } while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[j], c->lookup_props, &property)); 364 365 found_non_mark |= !(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK); 366 367 if (likely (c->buffer->info[j].codepoint != component[i])) 368 return false; 369 } 370 371 if (first_was_mark && found_non_mark) 372 c->guess_glyph_class (HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE); 373 374 /* Allocate new ligature id */ 375 unsigned int lig_id = allocate_lig_id (c->buffer); 376 c->buffer->info[c->buffer->idx].lig_comp() = 0; 377 c->buffer->info[c->buffer->idx].lig_id() = lig_id; 378 379 if (j < c->buffer->idx + count) /* No input glyphs skipped */ 380 { 381 c->replace_glyphs_be16 (count, 1, (const uint16_t *) &ligGlyph); 382 } 383 else 384 { 385 c->replace_glyph (ligGlyph); 386 387 /* Now we must do a second loop to copy the skipped glyphs to 388 `out' and assign component values to it. We start with the 389 glyph after the first component. Glyphs between component 390 i and i+1 belong to component i. Together with the lig_id 391 value it is later possible to check whether a specific 392 component value really belongs to a given ligature. */ 393 394 for (unsigned int i = 1; i < count; i++) 395 { 396 while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[c->buffer->idx], c->lookup_props, NULL)) 397 { 398 c->buffer->info[c->buffer->idx].lig_comp() = i; 399 c->buffer->info[c->buffer->idx].lig_id() = lig_id; 400 c->replace_glyph (c->buffer->info[c->buffer->idx].codepoint); 401 } 402 403 /* Skip the base glyph */ 404 c->buffer->idx++; 405 } 406 } 407 408 return true; 409 } 410 411 public: 412 inline bool sanitize (hb_sanitize_context_t *c) { 413 TRACE_SANITIZE (); 414 return ligGlyph.sanitize (c) 415 && component.sanitize (c); 416 } 417 418 private: 419 GlyphID ligGlyph; /* GlyphID of ligature to substitute */ 420 HeadlessArrayOf<GlyphID> 421 component; /* Array of component GlyphIDs--start 422 * with the second component--ordered 423 * in writing direction */ 424 public: 425 DEFINE_SIZE_ARRAY (4, component); 426}; 427 428struct LigatureSet 429{ 430 friend struct LigatureSubstFormat1; 431 432 private: 433 inline bool apply (hb_apply_context_t *c) const 434 { 435 TRACE_APPLY (); 436 unsigned int num_ligs = ligature.len; 437 for (unsigned int i = 0; i < num_ligs; i++) 438 { 439 const Ligature &lig = this+ligature[i]; 440 if (lig.apply (c)) 441 return true; 442 } 443 444 return false; 445 } 446 447 public: 448 inline bool sanitize (hb_sanitize_context_t *c) { 449 TRACE_SANITIZE (); 450 return ligature.sanitize (c, this); 451 } 452 453 private: 454 OffsetArrayOf<Ligature> 455 ligature; /* Array LigatureSet tables 456 * ordered by preference */ 457 public: 458 DEFINE_SIZE_ARRAY (2, ligature); 459}; 460 461struct LigatureSubstFormat1 462{ 463 friend struct LigatureSubst; 464 465 private: 466 inline bool apply (hb_apply_context_t *c) const 467 { 468 TRACE_APPLY (); 469 hb_codepoint_t glyph_id = c->buffer->info[c->buffer->idx].codepoint; 470 471 unsigned int index = (this+coverage) (glyph_id); 472 if (likely (index == NOT_COVERED)) 473 return false; 474 475 const LigatureSet &lig_set = this+ligatureSet[index]; 476 return lig_set.apply (c); 477 } 478 479 inline bool sanitize (hb_sanitize_context_t *c) { 480 TRACE_SANITIZE (); 481 return coverage.sanitize (c, this) 482 && ligatureSet.sanitize (c, this); 483 } 484 485 private: 486 USHORT format; /* Format identifier--format = 1 */ 487 OffsetTo<Coverage> 488 coverage; /* Offset to Coverage table--from 489 * beginning of Substitution table */ 490 OffsetArrayOf<LigatureSet> 491 ligatureSet; /* Array LigatureSet tables 492 * ordered by Coverage Index */ 493 public: 494 DEFINE_SIZE_ARRAY (6, ligatureSet); 495}; 496 497struct LigatureSubst 498{ 499 friend struct SubstLookupSubTable; 500 501 private: 502 inline bool apply (hb_apply_context_t *c) const 503 { 504 TRACE_APPLY (); 505 switch (u.format) { 506 case 1: return u.format1.apply (c); 507 default:return false; 508 } 509 } 510 511 inline bool sanitize (hb_sanitize_context_t *c) { 512 TRACE_SANITIZE (); 513 if (!u.format.sanitize (c)) return false; 514 switch (u.format) { 515 case 1: return u.format1.sanitize (c); 516 default:return true; 517 } 518 } 519 520 private: 521 union { 522 USHORT format; /* Format identifier */ 523 LigatureSubstFormat1 format1; 524 } u; 525}; 526 527 528static inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index); 529 530struct ContextSubst : Context 531{ 532 friend struct SubstLookupSubTable; 533 534 private: 535 inline bool apply (hb_apply_context_t *c) const 536 { 537 TRACE_APPLY (); 538 return Context::apply (c, substitute_lookup); 539 } 540}; 541 542struct ChainContextSubst : ChainContext 543{ 544 friend struct SubstLookupSubTable; 545 546 private: 547 inline bool apply (hb_apply_context_t *c) const 548 { 549 TRACE_APPLY (); 550 return ChainContext::apply (c, substitute_lookup); 551 } 552}; 553 554 555struct ExtensionSubst : Extension 556{ 557 friend struct SubstLookupSubTable; 558 friend struct SubstLookup; 559 560 private: 561 inline const struct SubstLookupSubTable& get_subtable (void) const 562 { 563 unsigned int offset = get_offset (); 564 if (unlikely (!offset)) return Null(SubstLookupSubTable); 565 return StructAtOffset<SubstLookupSubTable> (this, offset); 566 } 567 568 inline bool apply (hb_apply_context_t *c) const; 569 570 inline bool sanitize (hb_sanitize_context_t *c); 571 572 inline bool is_reverse (void) const; 573}; 574 575 576struct ReverseChainSingleSubstFormat1 577{ 578 friend struct ReverseChainSingleSubst; 579 580 private: 581 inline bool apply (hb_apply_context_t *c) const 582 { 583 TRACE_APPLY (); 584 if (unlikely (c->context_length != NO_CONTEXT)) 585 return false; /* No chaining to this type */ 586 587 unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint); 588 if (likely (index == NOT_COVERED)) 589 return false; 590 591 const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack); 592 const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead); 593 594 if (match_backtrack (c, 595 backtrack.len, (USHORT *) backtrack.array, 596 match_coverage, this) && 597 match_lookahead (c, 598 lookahead.len, (USHORT *) lookahead.array, 599 match_coverage, this, 600 1)) 601 { 602 c->buffer->info[c->buffer->idx].codepoint = substitute[index]; 603 c->buffer->idx--; /* Reverse! */ 604 return true; 605 } 606 607 return false; 608 } 609 610 inline bool sanitize (hb_sanitize_context_t *c) { 611 TRACE_SANITIZE (); 612 if (!(coverage.sanitize (c, this) 613 && backtrack.sanitize (c, this))) 614 return false; 615 OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack); 616 if (!lookahead.sanitize (c, this)) 617 return false; 618 ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead); 619 return substitute.sanitize (c); 620 } 621 622 private: 623 USHORT format; /* Format identifier--format = 1 */ 624 OffsetTo<Coverage> 625 coverage; /* Offset to Coverage table--from 626 * beginning of table */ 627 OffsetArrayOf<Coverage> 628 backtrack; /* Array of coverage tables 629 * in backtracking sequence, in glyph 630 * sequence order */ 631 OffsetArrayOf<Coverage> 632 lookaheadX; /* Array of coverage tables 633 * in lookahead sequence, in glyph 634 * sequence order */ 635 ArrayOf<GlyphID> 636 substituteX; /* Array of substitute 637 * GlyphIDs--ordered by Coverage Index */ 638 public: 639 DEFINE_SIZE_MIN (10); 640}; 641 642struct ReverseChainSingleSubst 643{ 644 friend struct SubstLookupSubTable; 645 646 private: 647 inline bool apply (hb_apply_context_t *c) const 648 { 649 TRACE_APPLY (); 650 switch (u.format) { 651 case 1: return u.format1.apply (c); 652 default:return false; 653 } 654 } 655 656 inline bool sanitize (hb_sanitize_context_t *c) { 657 TRACE_SANITIZE (); 658 if (!u.format.sanitize (c)) return false; 659 switch (u.format) { 660 case 1: return u.format1.sanitize (c); 661 default:return true; 662 } 663 } 664 665 private: 666 union { 667 USHORT format; /* Format identifier */ 668 ReverseChainSingleSubstFormat1 format1; 669 } u; 670}; 671 672 673 674/* 675 * SubstLookup 676 */ 677 678struct SubstLookupSubTable 679{ 680 friend struct SubstLookup; 681 682 enum { 683 Single = 1, 684 Multiple = 2, 685 Alternate = 3, 686 Ligature = 4, 687 Context = 5, 688 ChainContext = 6, 689 Extension = 7, 690 ReverseChainSingle = 8 691 }; 692 693 inline bool apply (hb_apply_context_t *c, unsigned int lookup_type) const 694 { 695 TRACE_APPLY (); 696 switch (lookup_type) { 697 case Single: return u.single.apply (c); 698 case Multiple: return u.multiple.apply (c); 699 case Alternate: return u.alternate.apply (c); 700 case Ligature: return u.ligature.apply (c); 701 case Context: return u.c.apply (c); 702 case ChainContext: return u.chainContext.apply (c); 703 case Extension: return u.extension.apply (c); 704 case ReverseChainSingle: return u.reverseChainContextSingle.apply (c); 705 default:return false; 706 } 707 } 708 709 inline bool sanitize (hb_sanitize_context_t *c, unsigned int lookup_type) { 710 TRACE_SANITIZE (); 711 switch (lookup_type) { 712 case Single: return u.single.sanitize (c); 713 case Multiple: return u.multiple.sanitize (c); 714 case Alternate: return u.alternate.sanitize (c); 715 case Ligature: return u.ligature.sanitize (c); 716 case Context: return u.c.sanitize (c); 717 case ChainContext: return u.chainContext.sanitize (c); 718 case Extension: return u.extension.sanitize (c); 719 case ReverseChainSingle: return u.reverseChainContextSingle.sanitize (c); 720 default:return true; 721 } 722 } 723 724 private: 725 union { 726 USHORT sub_format; 727 SingleSubst single; 728 MultipleSubst multiple; 729 AlternateSubst alternate; 730 LigatureSubst ligature; 731 ContextSubst c; 732 ChainContextSubst chainContext; 733 ExtensionSubst extension; 734 ReverseChainSingleSubst reverseChainContextSingle; 735 } u; 736 public: 737 DEFINE_SIZE_UNION (2, sub_format); 738}; 739 740 741struct SubstLookup : Lookup 742{ 743 inline const SubstLookupSubTable& get_subtable (unsigned int i) const 744 { return this+CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable)[i]; } 745 746 inline static bool lookup_type_is_reverse (unsigned int lookup_type) 747 { return lookup_type == SubstLookupSubTable::ReverseChainSingle; } 748 749 inline bool is_reverse (void) const 750 { 751 unsigned int type = get_type (); 752 if (unlikely (type == SubstLookupSubTable::Extension)) 753 return CastR<ExtensionSubst> (get_subtable(0)).is_reverse (); 754 return lookup_type_is_reverse (type); 755 } 756 757 758 inline bool apply_once (hb_face_t *face, 759 hb_buffer_t *buffer, 760 hb_mask_t lookup_mask, 761 unsigned int context_length, 762 unsigned int nesting_level_left) const 763 { 764 unsigned int lookup_type = get_type (); 765 hb_apply_context_t c[1] = {{0}}; 766 767 c->face = face; 768 c->buffer = buffer; 769 c->direction = buffer->props.direction; 770 c->lookup_mask = lookup_mask; 771 c->context_length = context_length; 772 c->nesting_level_left = nesting_level_left; 773 c->lookup_props = get_props (); 774 775 if (!_hb_ot_layout_check_glyph_property (c->face, &c->buffer->info[c->buffer->idx], c->lookup_props, &c->property)) 776 return false; 777 778 if (unlikely (lookup_type == SubstLookupSubTable::Extension)) 779 { 780 /* The spec says all subtables should have the same type. 781 * This is specially important if one has a reverse type! 782 * 783 * This is rather slow to do this here for every glyph, 784 * but it's easiest, and who uses extension lookups anyway?!*/ 785 unsigned int count = get_subtable_count (); 786 unsigned int type = get_subtable(0).u.extension.get_type (); 787 for (unsigned int i = 1; i < count; i++) 788 if (get_subtable(i).u.extension.get_type () != type) 789 return false; 790 } 791 792 unsigned int count = get_subtable_count (); 793 for (unsigned int i = 0; i < count; i++) 794 if (get_subtable (i).apply (c, lookup_type)) 795 return true; 796 797 return false; 798 } 799 800 inline bool apply_string (hb_face_t *face, 801 hb_buffer_t *buffer, 802 hb_mask_t mask) const 803 { 804 bool ret = false; 805 806 if (unlikely (!buffer->len)) 807 return false; 808 809 if (likely (!is_reverse ())) 810 { 811 /* in/out forward substitution */ 812 buffer->clear_output (); 813 buffer->idx = 0; 814 while (buffer->idx < buffer->len) 815 { 816 if ((buffer->info[buffer->idx].mask & mask) && 817 apply_once (face, buffer, mask, NO_CONTEXT, MAX_NESTING_LEVEL)) 818 ret = true; 819 else 820 buffer->next_glyph (); 821 822 } 823 if (ret) 824 buffer->swap_buffers (); 825 } 826 else 827 { 828 /* in-place backward substitution */ 829 buffer->idx = buffer->len - 1; 830 do 831 { 832 if ((buffer->info[buffer->idx].mask & mask) && 833 apply_once (face, buffer, mask, NO_CONTEXT, MAX_NESTING_LEVEL)) 834 ret = true; 835 else 836 buffer->idx--; 837 838 } 839 while ((int) buffer->idx >= 0); 840 } 841 842 return ret; 843 } 844 845 inline bool sanitize (hb_sanitize_context_t *c) { 846 TRACE_SANITIZE (); 847 if (unlikely (!Lookup::sanitize (c))) return false; 848 OffsetArrayOf<SubstLookupSubTable> &list = CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable); 849 return list.sanitize (c, this, get_type ()); 850 } 851}; 852 853typedef OffsetListOf<SubstLookup> SubstLookupList; 854 855/* 856 * GSUB -- The Glyph Substitution Table 857 */ 858 859struct GSUB : GSUBGPOS 860{ 861 static const hb_tag_t Tag = HB_OT_TAG_GSUB; 862 863 inline const SubstLookup& get_lookup (unsigned int i) const 864 { return CastR<SubstLookup> (GSUBGPOS::get_lookup (i)); } 865 866 inline bool substitute_lookup (hb_face_t *face, 867 hb_buffer_t *buffer, 868 unsigned int lookup_index, 869 hb_mask_t mask) const 870 { return get_lookup (lookup_index).apply_string (face, buffer, mask); } 871 872 static inline void substitute_start (hb_buffer_t *buffer); 873 static inline void substitute_finish (hb_buffer_t *buffer); 874 875 inline bool sanitize (hb_sanitize_context_t *c) { 876 TRACE_SANITIZE (); 877 if (unlikely (!GSUBGPOS::sanitize (c))) return false; 878 OffsetTo<SubstLookupList> &list = CastR<OffsetTo<SubstLookupList> > (lookupList); 879 return list.sanitize (c, this); 880 } 881 public: 882 DEFINE_SIZE_STATIC (10); 883}; 884 885 886void 887GSUB::substitute_start (hb_buffer_t *buffer) 888{ 889 HB_BUFFER_ALLOCATE_VAR (buffer, props_cache); 890 HB_BUFFER_ALLOCATE_VAR (buffer, lig_id); 891 HB_BUFFER_ALLOCATE_VAR (buffer, lig_comp); 892 893 unsigned int count = buffer->len; 894 for (unsigned int i = 0; i < count; i++) 895 buffer->info[i].props_cache() = buffer->info[i].lig_id() = buffer->info[i].lig_comp() = 0; 896} 897 898void 899GSUB::substitute_finish (hb_buffer_t *buffer) 900{ 901} 902 903 904/* Out-of-class implementation for methods recursing */ 905 906inline bool ExtensionSubst::apply (hb_apply_context_t *c) const 907{ 908 TRACE_APPLY (); 909 return get_subtable ().apply (c, get_type ()); 910} 911 912inline bool ExtensionSubst::sanitize (hb_sanitize_context_t *c) 913{ 914 TRACE_SANITIZE (); 915 if (unlikely (!Extension::sanitize (c))) return false; 916 unsigned int offset = get_offset (); 917 if (unlikely (!offset)) return true; 918 return StructAtOffset<SubstLookupSubTable> (this, offset).sanitize (c, get_type ()); 919} 920 921inline bool ExtensionSubst::is_reverse (void) const 922{ 923 unsigned int type = get_type (); 924 if (unlikely (type == SubstLookupSubTable::Extension)) 925 return CastR<ExtensionSubst> (get_subtable()).is_reverse (); 926 return SubstLookup::lookup_type_is_reverse (type); 927} 928 929static inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index) 930{ 931 const GSUB &gsub = *(c->face->ot_layout->gsub); 932 const SubstLookup &l = gsub.get_lookup (lookup_index); 933 934 if (unlikely (c->nesting_level_left == 0)) 935 return false; 936 937 if (unlikely (c->context_length < 1)) 938 return false; 939 940 return l.apply_once (c->face, c->buffer, c->lookup_mask, c->context_length, c->nesting_level_left - 1); 941} 942 943 944 945#endif /* HB_OT_LAYOUT_GSUB_TABLE_HH */ 946