hb-ot-layout-gsub-table.hh revision f211d5c291b4c947cfd732e873627567173057e4
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 bool would_apply (hb_codepoint_t glyph_id) const 54 { 55 return (this+coverage) (glyph_id) != NOT_COVERED; 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 private: 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 bool would_apply (hb_codepoint_t glyph_id) const 106 { 107 return (this+coverage) (glyph_id) != NOT_COVERED; 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 private: 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 bool would_apply (hb_codepoint_t glyph_id) const 159 { 160 switch (u.format) { 161 case 1: return u.format1.would_apply (glyph_id); 162 case 2: return u.format2.would_apply (glyph_id); 163 default:return false; 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 private: 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 (c->buffer->cur(), 0, 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 private: 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 bool would_apply (hb_codepoint_t glyph_id) const 256 { 257 return (this+coverage) (glyph_id) != NOT_COVERED; 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 private: 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 bool would_apply (hb_codepoint_t glyph_id) const 303 { 304 switch (u.format) { 305 case 1: return u.format1.would_apply (glyph_id); 306 default:return false; 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 private: 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 bool would_apply (hb_codepoint_t glyph_id) const 360 { 361 return (this+coverage) (glyph_id) != NOT_COVERED; 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 private: 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 bool would_apply (hb_codepoint_t glyph_id) const 425 { 426 switch (u.format) { 427 case 1: return u.format1.would_apply (glyph_id); 428 default:return false; 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 private: 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_codepoint_t second) const 475 { 476 return component.len == 2 && component[1] == 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 < 2)) 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 bool first_was_mark = (c->property & HB_OT_LAYOUT_GLYPH_CLASS_MARK); 489 bool found_non_mark = false; 490 491 for (unsigned int i = 1; i < count; i++) 492 { 493 unsigned int property; 494 495 if (!skippy_iter.next (&property)) return TRACE_RETURN (false); 496 497 found_non_mark |= !(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK); 498 499 if (likely (c->buffer->info[skippy_iter.idx].codepoint != component[i])) return TRACE_RETURN (false); 500 } 501 502 unsigned int klass = first_was_mark && found_non_mark ? HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE : 0; 503 504 /* Allocate new ligature id */ 505 unsigned int lig_id = allocate_lig_id (c->buffer); 506 set_lig_props (c->buffer->cur(), lig_id, 0); 507 508 if (skippy_iter.idx < c->buffer->idx + count) /* No input glyphs skipped */ 509 { 510 hb_codepoint_t lig_glyph = ligGlyph; 511 c->replace_glyphs (count, 1, &lig_glyph, klass); 512 } 513 else 514 { 515 c->buffer->merge_clusters (c->buffer->idx, skippy_iter.idx + 1); 516 c->replace_glyph (ligGlyph); 517 518 /* Now we must do a second loop to copy the skipped glyphs to 519 `out' and assign component values to it. We start with the 520 glyph after the first component. Glyphs between component 521 i and i+1 belong to component i. Together with the lig_id 522 value it is later possible to check whether a specific 523 component value really belongs to a given ligature. */ 524 525 for (unsigned int i = 1; i < count; i++) 526 { 527 while (c->should_mark_skip_current_glyph ()) 528 { 529 set_lig_props (c->buffer->cur(), lig_id, i); 530 c->buffer->next_glyph (); 531 } 532 533 /* Skip the base glyph */ 534 c->buffer->idx++; 535 } 536 } 537 538 return TRACE_RETURN (true); 539 } 540 541 public: 542 inline bool sanitize (hb_sanitize_context_t *c) { 543 TRACE_SANITIZE (); 544 return TRACE_RETURN (ligGlyph.sanitize (c) && component.sanitize (c)); 545 } 546 547 private: 548 GlyphID ligGlyph; /* GlyphID of ligature to substitute */ 549 HeadlessArrayOf<GlyphID> 550 component; /* Array of component GlyphIDs--start 551 * with the second component--ordered 552 * in writing direction */ 553 public: 554 DEFINE_SIZE_ARRAY (4, component); 555}; 556 557struct LigatureSet 558{ 559 friend struct LigatureSubstFormat1; 560 561 private: 562 563 inline void closure (hb_closure_context_t *c) const 564 { 565 TRACE_CLOSURE (); 566 unsigned int num_ligs = ligature.len; 567 for (unsigned int i = 0; i < num_ligs; i++) 568 (this+ligature[i]).closure (c); 569 } 570 571 inline bool would_apply (hb_codepoint_t second) const 572 { 573 unsigned int num_ligs = ligature.len; 574 for (unsigned int i = 0; i < num_ligs; i++) 575 { 576 const Ligature &lig = this+ligature[i]; 577 if (lig.would_apply (second)) 578 return true; 579 } 580 return false; 581 } 582 583 inline bool apply (hb_apply_context_t *c) const 584 { 585 TRACE_APPLY (); 586 unsigned int num_ligs = ligature.len; 587 for (unsigned int i = 0; i < num_ligs; i++) 588 { 589 const Ligature &lig = this+ligature[i]; 590 if (lig.apply (c)) return TRACE_RETURN (true); 591 } 592 593 return TRACE_RETURN (false); 594 } 595 596 public: 597 inline bool sanitize (hb_sanitize_context_t *c) { 598 TRACE_SANITIZE (); 599 return TRACE_RETURN (ligature.sanitize (c, this)); 600 } 601 602 private: 603 OffsetArrayOf<Ligature> 604 ligature; /* Array LigatureSet tables 605 * ordered by preference */ 606 public: 607 DEFINE_SIZE_ARRAY (2, ligature); 608}; 609 610struct LigatureSubstFormat1 611{ 612 friend struct LigatureSubst; 613 614 private: 615 616 inline void closure (hb_closure_context_t *c) const 617 { 618 TRACE_CLOSURE (); 619 Coverage::Iter iter; 620 for (iter.init (this+coverage); iter.more (); iter.next ()) { 621 if (c->glyphs->has (iter.get_glyph ())) 622 (this+ligatureSet[iter.get_coverage ()]).closure (c); 623 } 624 } 625 626 inline bool would_apply (hb_codepoint_t first, hb_codepoint_t second) const 627 { 628 unsigned int index; 629 return (index = (this+coverage) (first)) != NOT_COVERED && 630 (this+ligatureSet[index]).would_apply (second); 631 } 632 633 inline bool apply (hb_apply_context_t *c) const 634 { 635 TRACE_APPLY (); 636 hb_codepoint_t glyph_id = c->buffer->cur().codepoint; 637 638 unsigned int index = (this+coverage) (glyph_id); 639 if (likely (index == NOT_COVERED)) return TRACE_RETURN (false); 640 641 const LigatureSet &lig_set = this+ligatureSet[index]; 642 return TRACE_RETURN (lig_set.apply (c)); 643 } 644 645 inline bool sanitize (hb_sanitize_context_t *c) { 646 TRACE_SANITIZE (); 647 return TRACE_RETURN (coverage.sanitize (c, this) && ligatureSet.sanitize (c, this)); 648 } 649 650 private: 651 USHORT format; /* Format identifier--format = 1 */ 652 OffsetTo<Coverage> 653 coverage; /* Offset to Coverage table--from 654 * beginning of Substitution table */ 655 OffsetArrayOf<LigatureSet> 656 ligatureSet; /* Array LigatureSet tables 657 * ordered by Coverage Index */ 658 public: 659 DEFINE_SIZE_ARRAY (6, ligatureSet); 660}; 661 662struct LigatureSubst 663{ 664 friend struct SubstLookupSubTable; 665 666 private: 667 668 inline void closure (hb_closure_context_t *c) const 669 { 670 TRACE_CLOSURE (); 671 switch (u.format) { 672 case 1: u.format1.closure (c); break; 673 default: break; 674 } 675 } 676 677 inline bool would_apply (hb_codepoint_t first, hb_codepoint_t second) const 678 { 679 switch (u.format) { 680 case 1: return u.format1.would_apply (first, second); 681 default:return false; 682 } 683 } 684 685 inline bool apply (hb_apply_context_t *c) const 686 { 687 TRACE_APPLY (); 688 switch (u.format) { 689 case 1: return TRACE_RETURN (u.format1.apply (c)); 690 default:return TRACE_RETURN (false); 691 } 692 } 693 694 inline bool sanitize (hb_sanitize_context_t *c) { 695 TRACE_SANITIZE (); 696 if (!u.format.sanitize (c)) return TRACE_RETURN (false); 697 switch (u.format) { 698 case 1: return TRACE_RETURN (u.format1.sanitize (c)); 699 default:return TRACE_RETURN (true); 700 } 701 } 702 703 private: 704 union { 705 USHORT format; /* Format identifier */ 706 LigatureSubstFormat1 format1; 707 } u; 708}; 709 710 711static inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index); 712static inline void closure_lookup (hb_closure_context_t *c, unsigned int lookup_index); 713 714struct ContextSubst : Context 715{ 716 friend struct SubstLookupSubTable; 717 718 private: 719 720 inline void closure (hb_closure_context_t *c) const 721 { 722 TRACE_CLOSURE (); 723 return Context::closure (c, closure_lookup); 724 } 725 726 inline bool apply (hb_apply_context_t *c) const 727 { 728 TRACE_APPLY (); 729 return TRACE_RETURN (Context::apply (c, substitute_lookup)); 730 } 731}; 732 733struct ChainContextSubst : ChainContext 734{ 735 friend struct SubstLookupSubTable; 736 737 private: 738 739 inline void closure (hb_closure_context_t *c) const 740 { 741 TRACE_CLOSURE (); 742 return ChainContext::closure (c, closure_lookup); 743 } 744 745 inline bool apply (hb_apply_context_t *c) const 746 { 747 TRACE_APPLY (); 748 return TRACE_RETURN (ChainContext::apply (c, substitute_lookup)); 749 } 750}; 751 752 753struct ExtensionSubst : Extension 754{ 755 friend struct SubstLookupSubTable; 756 friend struct SubstLookup; 757 758 private: 759 inline const struct SubstLookupSubTable& get_subtable (void) const 760 { 761 unsigned int offset = get_offset (); 762 if (unlikely (!offset)) return Null(SubstLookupSubTable); 763 return StructAtOffset<SubstLookupSubTable> (this, offset); 764 } 765 766 inline void closure (hb_closure_context_t *c) const; 767 inline bool would_apply (hb_codepoint_t glyph_id) const; 768 inline bool would_apply (hb_codepoint_t first, hb_codepoint_t second) const; 769 770 inline bool apply (hb_apply_context_t *c) const; 771 772 inline bool sanitize (hb_sanitize_context_t *c); 773 774 inline bool is_reverse (void) const; 775}; 776 777 778struct ReverseChainSingleSubstFormat1 779{ 780 friend struct ReverseChainSingleSubst; 781 782 private: 783 784 inline void closure (hb_closure_context_t *c) const 785 { 786 TRACE_CLOSURE (); 787 const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack); 788 789 unsigned int count; 790 791 count = backtrack.len; 792 for (unsigned int i = 0; i < count; i++) 793 if (!(this+backtrack[i]).intersects (c->glyphs)) 794 return; 795 796 count = lookahead.len; 797 for (unsigned int i = 0; i < count; i++) 798 if (!(this+lookahead[i]).intersects (c->glyphs)) 799 return; 800 801 const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead); 802 Coverage::Iter iter; 803 for (iter.init (this+coverage); iter.more (); iter.next ()) { 804 if (c->glyphs->has (iter.get_glyph ())) 805 c->glyphs->add (substitute[iter.get_coverage ()]); 806 } 807 } 808 809 inline bool apply (hb_apply_context_t *c) const 810 { 811 TRACE_APPLY (); 812 if (unlikely (c->nesting_level_left != MAX_NESTING_LEVEL)) 813 return TRACE_RETURN (false); /* No chaining to this type */ 814 815 unsigned int index = (this+coverage) (c->buffer->cur().codepoint); 816 if (likely (index == NOT_COVERED)) return TRACE_RETURN (false); 817 818 const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack); 819 const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead); 820 821 if (match_backtrack (c, 822 backtrack.len, (USHORT *) backtrack.array, 823 match_coverage, this) && 824 match_lookahead (c, 825 lookahead.len, (USHORT *) lookahead.array, 826 match_coverage, this, 827 1)) 828 { 829 c->buffer->cur().codepoint = substitute[index]; 830 c->buffer->idx--; /* Reverse! */ 831 return TRACE_RETURN (true); 832 } 833 834 return TRACE_RETURN (false); 835 } 836 837 inline bool sanitize (hb_sanitize_context_t *c) { 838 TRACE_SANITIZE (); 839 if (!(coverage.sanitize (c, this) && backtrack.sanitize (c, this))) 840 return TRACE_RETURN (false); 841 OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack); 842 if (!lookahead.sanitize (c, this)) 843 return TRACE_RETURN (false); 844 ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead); 845 return TRACE_RETURN (substitute.sanitize (c)); 846 } 847 848 private: 849 USHORT format; /* Format identifier--format = 1 */ 850 OffsetTo<Coverage> 851 coverage; /* Offset to Coverage table--from 852 * beginning of table */ 853 OffsetArrayOf<Coverage> 854 backtrack; /* Array of coverage tables 855 * in backtracking sequence, in glyph 856 * sequence order */ 857 OffsetArrayOf<Coverage> 858 lookaheadX; /* Array of coverage tables 859 * in lookahead sequence, in glyph 860 * sequence order */ 861 ArrayOf<GlyphID> 862 substituteX; /* Array of substitute 863 * GlyphIDs--ordered by Coverage Index */ 864 public: 865 DEFINE_SIZE_MIN (10); 866}; 867 868struct ReverseChainSingleSubst 869{ 870 friend struct SubstLookupSubTable; 871 872 private: 873 874 inline void closure (hb_closure_context_t *c) const 875 { 876 TRACE_CLOSURE (); 877 switch (u.format) { 878 case 1: u.format1.closure (c); break; 879 default: break; 880 } 881 } 882 883 inline bool apply (hb_apply_context_t *c) const 884 { 885 TRACE_APPLY (); 886 switch (u.format) { 887 case 1: return TRACE_RETURN (u.format1.apply (c)); 888 default:return TRACE_RETURN (false); 889 } 890 } 891 892 inline bool sanitize (hb_sanitize_context_t *c) { 893 TRACE_SANITIZE (); 894 if (!u.format.sanitize (c)) return TRACE_RETURN (false); 895 switch (u.format) { 896 case 1: return TRACE_RETURN (u.format1.sanitize (c)); 897 default:return TRACE_RETURN (true); 898 } 899 } 900 901 private: 902 union { 903 USHORT format; /* Format identifier */ 904 ReverseChainSingleSubstFormat1 format1; 905 } u; 906}; 907 908 909 910/* 911 * SubstLookup 912 */ 913 914struct SubstLookupSubTable 915{ 916 friend struct SubstLookup; 917 918 enum Type { 919 Single = 1, 920 Multiple = 2, 921 Alternate = 3, 922 Ligature = 4, 923 Context = 5, 924 ChainContext = 6, 925 Extension = 7, 926 ReverseChainSingle = 8 927 }; 928 929 inline void closure (hb_closure_context_t *c, 930 unsigned int lookup_type) const 931 { 932 TRACE_CLOSURE (); 933 switch (lookup_type) { 934 case Single: u.single.closure (c); break; 935 case Multiple: u.multiple.closure (c); break; 936 case Alternate: u.alternate.closure (c); break; 937 case Ligature: u.ligature.closure (c); break; 938 case Context: u.c.closure (c); break; 939 case ChainContext: u.chainContext.closure (c); break; 940 case Extension: u.extension.closure (c); break; 941 case ReverseChainSingle: u.reverseChainContextSingle.closure (c); break; 942 default: break; 943 } 944 } 945 946 inline bool would_apply (hb_codepoint_t glyph_id, 947 unsigned int lookup_type) const 948 { 949 switch (lookup_type) { 950 case Single: return u.single.would_apply (glyph_id); 951 case Multiple: return u.multiple.would_apply (glyph_id); 952 case Alternate: return u.alternate.would_apply (glyph_id); 953 case Extension: return u.extension.would_apply (glyph_id); 954 default: return false; 955 } 956 } 957 inline bool would_apply (hb_codepoint_t first, 958 hb_codepoint_t second, 959 unsigned int lookup_type) const 960 { 961 switch (lookup_type) { 962 case Ligature: return u.ligature.would_apply (first, second); 963 case Extension: return u.extension.would_apply (first, second); 964 default: return false; 965 } 966 } 967 968 inline bool can_use_fast_path (unsigned int lookup_type) const 969 { 970 /* Fast path, for those that have coverage in the same place. 971 * Note that ReverseChainSingle can also go through this but 972 * it's not worth the effort. */ 973 return likely (lookup_type && lookup_type < Context) || 974 (hb_in_range<unsigned int> (lookup_type, Context, ChainContext) && 975 hb_in_range<unsigned int> (u.header.sub_format, 1, 2)); 976 } 977 978 inline bool apply (hb_apply_context_t *c, unsigned int lookup_type) const 979 { 980 TRACE_APPLY (); 981 if (can_use_fast_path (lookup_type)) 982 { 983 hb_codepoint_t glyph_id = c->buffer->cur().codepoint; 984 unsigned int index = (this+u.header.coverage) (glyph_id); 985 if (likely (index == NOT_COVERED)) return TRACE_RETURN (false); 986 } 987 switch (lookup_type) { 988 case Single: return TRACE_RETURN (u.single.apply (c)); 989 case Multiple: return TRACE_RETURN (u.multiple.apply (c)); 990 case Alternate: return TRACE_RETURN (u.alternate.apply (c)); 991 case Ligature: return TRACE_RETURN (u.ligature.apply (c)); 992 case Context: return TRACE_RETURN (u.c.apply (c)); 993 case ChainContext: return TRACE_RETURN (u.chainContext.apply (c)); 994 case Extension: return TRACE_RETURN (u.extension.apply (c)); 995 case ReverseChainSingle: return TRACE_RETURN (u.reverseChainContextSingle.apply (c)); 996 default: return TRACE_RETURN (false); 997 } 998 } 999 1000 inline bool sanitize (hb_sanitize_context_t *c, unsigned int lookup_type) { 1001 TRACE_SANITIZE (); 1002 if (!u.header.sub_format.sanitize (c) || 1003 (can_use_fast_path (lookup_type) && !u.header.coverage.sanitize (c, this))) 1004 return TRACE_RETURN (false); 1005 switch (lookup_type) { 1006 case Single: return TRACE_RETURN (u.single.sanitize (c)); 1007 case Multiple: return TRACE_RETURN (u.multiple.sanitize (c)); 1008 case Alternate: return TRACE_RETURN (u.alternate.sanitize (c)); 1009 case Ligature: return TRACE_RETURN (u.ligature.sanitize (c)); 1010 case Context: return TRACE_RETURN (u.c.sanitize (c)); 1011 case ChainContext: return TRACE_RETURN (u.chainContext.sanitize (c)); 1012 case Extension: return TRACE_RETURN (u.extension.sanitize (c)); 1013 case ReverseChainSingle: return TRACE_RETURN (u.reverseChainContextSingle.sanitize (c)); 1014 default: return TRACE_RETURN (true); 1015 } 1016 } 1017 1018 private: 1019 union { 1020 struct { 1021 USHORT sub_format; 1022 OffsetTo<Coverage> coverage; 1023 } header; 1024 SingleSubst single; 1025 MultipleSubst multiple; 1026 AlternateSubst alternate; 1027 LigatureSubst ligature; 1028 ContextSubst c; 1029 ChainContextSubst chainContext; 1030 ExtensionSubst extension; 1031 ReverseChainSingleSubst reverseChainContextSingle; 1032 } u; 1033 public: 1034 DEFINE_SIZE_UNION (2, header.sub_format); 1035}; 1036 1037 1038struct SubstLookup : Lookup 1039{ 1040 inline const SubstLookupSubTable& get_subtable (unsigned int i) const 1041 { return this+CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable)[i]; } 1042 1043 inline static bool lookup_type_is_reverse (unsigned int lookup_type) 1044 { return lookup_type == SubstLookupSubTable::ReverseChainSingle; } 1045 1046 inline bool is_reverse (void) const 1047 { 1048 unsigned int type = get_type (); 1049 if (unlikely (type == SubstLookupSubTable::Extension)) 1050 return CastR<ExtensionSubst> (get_subtable(0)).is_reverse (); 1051 return lookup_type_is_reverse (type); 1052 } 1053 1054 inline void closure (hb_closure_context_t *c) const 1055 { 1056 unsigned int lookup_type = get_type (); 1057 unsigned int count = get_subtable_count (); 1058 for (unsigned int i = 0; i < count; i++) 1059 get_subtable (i).closure (c, lookup_type); 1060 } 1061 1062 inline bool would_apply (hb_codepoint_t glyph_id) const 1063 { 1064 unsigned int lookup_type = get_type (); 1065 unsigned int count = get_subtable_count (); 1066 for (unsigned int i = 0; i < count; i++) 1067 if (get_subtable (i).would_apply (glyph_id, lookup_type)) 1068 return true; 1069 return false; 1070 } 1071 inline bool would_apply (hb_codepoint_t first, hb_codepoint_t second) const 1072 { 1073 unsigned int lookup_type = get_type (); 1074 unsigned int count = get_subtable_count (); 1075 for (unsigned int i = 0; i < count; i++) 1076 if (get_subtable (i).would_apply (first, second, lookup_type)) 1077 return true; 1078 return false; 1079 } 1080 1081 inline bool apply_once (hb_apply_context_t *c) const 1082 { 1083 unsigned int lookup_type = get_type (); 1084 1085 if (!_hb_ot_layout_check_glyph_property (c->face, &c->buffer->cur(), c->lookup_props, &c->property)) 1086 return false; 1087 1088 /* TODO: For the most common case this can move out of the main 1089 * loop, but it's not a big deal for now. */ 1090 if (unlikely (lookup_type == SubstLookupSubTable::Extension)) 1091 { 1092 /* The spec says all subtables should have the same type. 1093 * This is specially important if one has a reverse type! 1094 * 1095 * This is rather slow to do this here for every glyph, 1096 * but it's easiest, and who uses extension lookups anyway?!*/ 1097 unsigned int type = get_subtable(0).u.extension.get_type (); 1098 unsigned int count = get_subtable_count (); 1099 for (unsigned int i = 1; i < count; i++) 1100 if (get_subtable(i).u.extension.get_type () != type) 1101 return false; 1102 } 1103 1104 unsigned int count = get_subtable_count (); 1105 for (unsigned int i = 0; i < count; i++) 1106 if (get_subtable (i).apply (c, lookup_type)) 1107 return true; 1108 1109 return false; 1110 } 1111 1112 inline bool apply_string (hb_apply_context_t *c) const 1113 { 1114 bool ret = false; 1115 1116 if (unlikely (!c->buffer->len)) 1117 return false; 1118 1119 c->set_lookup (*this); 1120 1121 if (likely (!is_reverse ())) 1122 { 1123 /* in/out forward substitution */ 1124 c->buffer->clear_output (); 1125 c->buffer->idx = 0; 1126 while (c->buffer->idx < c->buffer->len) 1127 { 1128 if ((c->buffer->cur().mask & c->lookup_mask) && apply_once (c)) 1129 ret = true; 1130 else 1131 c->buffer->next_glyph (); 1132 1133 } 1134 if (ret) 1135 c->buffer->swap_buffers (); 1136 } 1137 else 1138 { 1139 /* in-place backward substitution */ 1140 c->buffer->idx = c->buffer->len - 1; 1141 do 1142 { 1143 if ((c->buffer->cur().mask & c->lookup_mask) && apply_once (c)) 1144 ret = true; 1145 else 1146 c->buffer->idx--; 1147 1148 } 1149 while ((int) c->buffer->idx >= 0); 1150 } 1151 1152 return ret; 1153 } 1154 1155 inline bool sanitize (hb_sanitize_context_t *c) { 1156 TRACE_SANITIZE (); 1157 if (unlikely (!Lookup::sanitize (c))) return TRACE_RETURN (false); 1158 OffsetArrayOf<SubstLookupSubTable> &list = CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable); 1159 return TRACE_RETURN (list.sanitize (c, this, get_type ())); 1160 } 1161}; 1162 1163typedef OffsetListOf<SubstLookup> SubstLookupList; 1164 1165/* 1166 * GSUB -- The Glyph Substitution Table 1167 */ 1168 1169struct GSUB : GSUBGPOS 1170{ 1171 static const hb_tag_t Tag = HB_OT_TAG_GSUB; 1172 1173 inline const SubstLookup& get_lookup (unsigned int i) const 1174 { return CastR<SubstLookup> (GSUBGPOS::get_lookup (i)); } 1175 1176 inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index) const 1177 { return get_lookup (lookup_index).apply_string (c); } 1178 1179 static inline void substitute_start (hb_buffer_t *buffer); 1180 static inline void substitute_finish (hb_buffer_t *buffer); 1181 1182 inline void closure_lookup (hb_closure_context_t *c, 1183 unsigned int lookup_index) const 1184 { return get_lookup (lookup_index).closure (c); } 1185 1186 inline bool sanitize (hb_sanitize_context_t *c) { 1187 TRACE_SANITIZE (); 1188 if (unlikely (!GSUBGPOS::sanitize (c))) return TRACE_RETURN (false); 1189 OffsetTo<SubstLookupList> &list = CastR<OffsetTo<SubstLookupList> > (lookupList); 1190 return TRACE_RETURN (list.sanitize (c, this)); 1191 } 1192 public: 1193 DEFINE_SIZE_STATIC (10); 1194}; 1195 1196 1197void 1198GSUB::substitute_start (hb_buffer_t *buffer) 1199{ 1200 HB_BUFFER_ALLOCATE_VAR (buffer, props_cache); 1201 HB_BUFFER_ALLOCATE_VAR (buffer, lig_props); 1202 HB_BUFFER_ALLOCATE_VAR (buffer, syllable); 1203 1204 unsigned int count = buffer->len; 1205 for (unsigned int i = 0; i < count; i++) 1206 buffer->info[i].props_cache() = buffer->info[i].lig_props() = buffer->info[i].syllable() = 0; 1207} 1208 1209void 1210GSUB::substitute_finish (hb_buffer_t *buffer HB_UNUSED) 1211{ 1212} 1213 1214 1215/* Out-of-class implementation for methods recursing */ 1216 1217inline void ExtensionSubst::closure (hb_closure_context_t *c) const 1218{ 1219 get_subtable ().closure (c, get_type ()); 1220} 1221 1222inline bool ExtensionSubst::would_apply (hb_codepoint_t glyph_id) const 1223{ 1224 return get_subtable ().would_apply (glyph_id, get_type ()); 1225} 1226 1227inline bool ExtensionSubst::would_apply (hb_codepoint_t first, hb_codepoint_t second) const 1228{ 1229 return get_subtable ().would_apply (first, second, get_type ()); 1230} 1231 1232inline bool ExtensionSubst::apply (hb_apply_context_t *c) const 1233{ 1234 TRACE_APPLY (); 1235 return TRACE_RETURN (get_subtable ().apply (c, get_type ())); 1236} 1237 1238inline bool ExtensionSubst::sanitize (hb_sanitize_context_t *c) 1239{ 1240 TRACE_SANITIZE (); 1241 if (unlikely (!Extension::sanitize (c))) return TRACE_RETURN (false); 1242 unsigned int offset = get_offset (); 1243 if (unlikely (!offset)) return TRACE_RETURN (true); 1244 return TRACE_RETURN (StructAtOffset<SubstLookupSubTable> (this, offset).sanitize (c, get_type ())); 1245} 1246 1247inline bool ExtensionSubst::is_reverse (void) const 1248{ 1249 unsigned int type = get_type (); 1250 if (unlikely (type == SubstLookupSubTable::Extension)) 1251 return CastR<ExtensionSubst> (get_subtable()).is_reverse (); 1252 return SubstLookup::lookup_type_is_reverse (type); 1253} 1254 1255static inline void closure_lookup (hb_closure_context_t *c, unsigned int lookup_index) 1256{ 1257 const GSUB &gsub = *(c->face->ot_layout->gsub); 1258 const SubstLookup &l = gsub.get_lookup (lookup_index); 1259 1260 if (unlikely (c->nesting_level_left == 0)) 1261 return; 1262 1263 c->nesting_level_left--; 1264 l.closure (c); 1265 c->nesting_level_left++; 1266} 1267 1268static inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index) 1269{ 1270 const GSUB &gsub = *(c->face->ot_layout->gsub); 1271 const SubstLookup &l = gsub.get_lookup (lookup_index); 1272 1273 if (unlikely (c->nesting_level_left == 0)) 1274 return false; 1275 1276 hb_apply_context_t new_c (*c); 1277 new_c.nesting_level_left--; 1278 new_c.set_lookup (l); 1279 return l.apply_once (&new_c); 1280} 1281 1282 1283 1284#endif /* HB_OT_LAYOUT_GSUB_TABLE_HH */ 1285