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