2 * Copyright © 2007,2008,2009,2010 Red Hat, Inc.
3 * Copyright © 2010,2012 Google, Inc.
5 * This is part of HarfBuzz, a text shaping library.
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.
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
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.
25 * Red Hat Author(s): Behdad Esfahbod
26 * Google Author(s): Behdad Esfahbod
29 #ifndef HB_OT_LAYOUT_GSUB_TABLE_HH
30 #define HB_OT_LAYOUT_GSUB_TABLE_HH
32 #include "hb-ot-layout-gsubgpos-private.hh"
36 struct SingleSubstFormat1
38 friend struct SingleSubst;
42 inline void closure (hb_closure_context_t *c) const
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);
53 inline bool would_apply (hb_codepoint_t glyph_id) const
55 return (this+coverage) (glyph_id) != NOT_COVERED;
58 inline bool apply (hb_apply_context_t *c) const
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);
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);
70 return TRACE_RETURN (true);
73 inline bool sanitize (hb_sanitize_context_t *c) {
75 return TRACE_RETURN (coverage.sanitize (c, this) && deltaGlyphID.sanitize (c));
79 USHORT format; /* Format identifier--format = 1 */
81 coverage; /* Offset to Coverage table--from
82 * beginning of Substitution table */
83 SHORT deltaGlyphID; /* Add to original GlyphID to get
84 * substitute GlyphID */
86 DEFINE_SIZE_STATIC (6);
89 struct SingleSubstFormat2
91 friend struct SingleSubst;
95 inline void closure (hb_closure_context_t *c) const
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 ()]);
105 inline bool would_apply (hb_codepoint_t glyph_id) const
107 return (this+coverage) (glyph_id) != NOT_COVERED;
110 inline bool apply (hb_apply_context_t *c) const
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);
117 if (unlikely (index >= substitute.len)) return TRACE_RETURN (false);
119 glyph_id = substitute[index];
120 c->replace_glyph (glyph_id);
122 return TRACE_RETURN (true);
125 inline bool sanitize (hb_sanitize_context_t *c) {
127 return TRACE_RETURN (coverage.sanitize (c, this) && substitute.sanitize (c));
131 USHORT format; /* Format identifier--format = 2 */
133 coverage; /* Offset to Coverage table--from
134 * beginning of Substitution table */
136 substitute; /* Array of substitute
137 * GlyphIDs--ordered by Coverage Index */
139 DEFINE_SIZE_ARRAY (6, substitute);
144 friend struct SubstLookupSubTable;
148 inline void closure (hb_closure_context_t *c) const
152 case 1: u.format1.closure (c); break;
153 case 2: u.format2.closure (c); break;
158 inline bool would_apply (hb_codepoint_t glyph_id) const
161 case 1: return u.format1.would_apply (glyph_id);
162 case 2: return u.format2.would_apply (glyph_id);
163 default:return false;
167 inline bool apply (hb_apply_context_t *c) const
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);
177 inline bool sanitize (hb_sanitize_context_t *c) {
179 if (!u.format.sanitize (c)) return TRACE_RETURN (false);
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);
189 USHORT format; /* Format identifier */
190 SingleSubstFormat1 format1;
191 SingleSubstFormat2 format2;
198 friend struct MultipleSubstFormat1;
202 inline void closure (hb_closure_context_t *c) const
205 unsigned int count = substitute.len;
206 for (unsigned int i = 0; i < count; i++)
207 c->glyphs->add (substitute[i]);
210 inline bool apply (hb_apply_context_t *c) const
213 if (unlikely (!substitute.len)) return TRACE_RETURN (false);
215 unsigned int klass = c->property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE ? HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH : 0;
216 c->replace_glyphs_be16 (1, substitute.len, (const uint16_t *) substitute.array, klass);
218 return TRACE_RETURN (true);
222 inline bool sanitize (hb_sanitize_context_t *c) {
224 return TRACE_RETURN (substitute.sanitize (c));
229 substitute; /* String of GlyphIDs to substitute */
231 DEFINE_SIZE_ARRAY (2, substitute);
234 struct MultipleSubstFormat1
236 friend struct MultipleSubst;
240 inline void closure (hb_closure_context_t *c) const
244 for (iter.init (this+coverage); iter.more (); iter.next ()) {
245 if (c->glyphs->has (iter.get_glyph ()))
246 (this+sequence[iter.get_coverage ()]).closure (c);
250 inline bool would_apply (hb_codepoint_t glyph_id) const
252 return (this+coverage) (glyph_id) != NOT_COVERED;
255 inline bool apply (hb_apply_context_t *c) const
259 unsigned int index = (this+coverage) (c->buffer->cur().codepoint);
260 if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
262 return TRACE_RETURN ((this+sequence[index]).apply (c));
265 inline bool sanitize (hb_sanitize_context_t *c) {
267 return TRACE_RETURN (coverage.sanitize (c, this) && sequence.sanitize (c, this));
271 USHORT format; /* Format identifier--format = 1 */
273 coverage; /* Offset to Coverage table--from
274 * beginning of Substitution table */
275 OffsetArrayOf<Sequence>
276 sequence; /* Array of Sequence tables
277 * ordered by Coverage Index */
279 DEFINE_SIZE_ARRAY (6, sequence);
284 friend struct SubstLookupSubTable;
288 inline void closure (hb_closure_context_t *c) const
292 case 1: u.format1.closure (c); break;
297 inline bool would_apply (hb_codepoint_t glyph_id) const
300 case 1: return u.format1.would_apply (glyph_id);
301 default:return false;
305 inline bool apply (hb_apply_context_t *c) const
309 case 1: return TRACE_RETURN (u.format1.apply (c));
310 default:return TRACE_RETURN (false);
314 inline bool sanitize (hb_sanitize_context_t *c) {
316 if (!u.format.sanitize (c)) return TRACE_RETURN (false);
318 case 1: return TRACE_RETURN (u.format1.sanitize (c));
319 default:return TRACE_RETURN (true);
325 USHORT format; /* Format identifier */
326 MultipleSubstFormat1 format1;
331 typedef ArrayOf<GlyphID> AlternateSet; /* Array of alternate GlyphIDs--in
334 struct AlternateSubstFormat1
336 friend struct AlternateSubst;
340 inline void closure (hb_closure_context_t *c) const
344 for (iter.init (this+coverage); iter.more (); iter.next ()) {
345 if (c->glyphs->has (iter.get_glyph ())) {
346 const AlternateSet &alt_set = this+alternateSet[iter.get_coverage ()];
347 unsigned int count = alt_set.len;
348 for (unsigned int i = 0; i < count; i++)
349 c->glyphs->add (alt_set[i]);
354 inline bool would_apply (hb_codepoint_t glyph_id) const
356 return (this+coverage) (glyph_id) != NOT_COVERED;
359 inline bool apply (hb_apply_context_t *c) const
362 hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
364 unsigned int index = (this+coverage) (glyph_id);
365 if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
367 const AlternateSet &alt_set = this+alternateSet[index];
369 if (unlikely (!alt_set.len)) return TRACE_RETURN (false);
371 hb_mask_t glyph_mask = c->buffer->cur().mask;
372 hb_mask_t lookup_mask = c->lookup_mask;
374 /* Note: This breaks badly if two features enabled this lookup together. */
375 unsigned int shift = _hb_ctz (lookup_mask);
376 unsigned int alt_index = ((lookup_mask & glyph_mask) >> shift);
378 if (unlikely (alt_index > alt_set.len || alt_index == 0)) return TRACE_RETURN (false);
380 glyph_id = alt_set[alt_index - 1];
382 c->replace_glyph (glyph_id);
384 return TRACE_RETURN (true);
387 inline bool sanitize (hb_sanitize_context_t *c) {
389 return TRACE_RETURN (coverage.sanitize (c, this) && alternateSet.sanitize (c, this));
393 USHORT format; /* Format identifier--format = 1 */
395 coverage; /* Offset to Coverage table--from
396 * beginning of Substitution table */
397 OffsetArrayOf<AlternateSet>
398 alternateSet; /* Array of AlternateSet tables
399 * ordered by Coverage Index */
401 DEFINE_SIZE_ARRAY (6, alternateSet);
404 struct AlternateSubst
406 friend struct SubstLookupSubTable;
410 inline void closure (hb_closure_context_t *c) const
414 case 1: u.format1.closure (c); break;
419 inline bool would_apply (hb_codepoint_t glyph_id) const
422 case 1: return u.format1.would_apply (glyph_id);
423 default:return false;
427 inline bool apply (hb_apply_context_t *c) const
431 case 1: return TRACE_RETURN (u.format1.apply (c));
432 default:return TRACE_RETURN (false);
436 inline bool sanitize (hb_sanitize_context_t *c) {
438 if (!u.format.sanitize (c)) return TRACE_RETURN (false);
440 case 1: return TRACE_RETURN (u.format1.sanitize (c));
441 default:return TRACE_RETURN (true);
447 USHORT format; /* Format identifier */
448 AlternateSubstFormat1 format1;
455 friend struct LigatureSet;
459 inline void closure (hb_closure_context_t *c) const
462 unsigned int count = component.len;
463 for (unsigned int i = 1; i < count; i++)
464 if (!c->glyphs->has (component[i]))
466 c->glyphs->add (ligGlyph);
469 inline bool would_apply (hb_codepoint_t second) const
471 return component.len == 2 && component[1] == second;
474 inline bool apply (hb_apply_context_t *c) const
477 unsigned int count = component.len;
478 if (unlikely (count < 2)) return TRACE_RETURN (false);
480 hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx, count - 1);
481 if (skippy_iter.has_no_chance ()) return TRACE_RETURN (false);
483 bool first_was_mark = (c->property & HB_OT_LAYOUT_GLYPH_CLASS_MARK);
484 bool found_non_mark = false;
486 for (unsigned int i = 1; i < count; i++)
488 unsigned int property;
490 if (!skippy_iter.next (&property)) return TRACE_RETURN (false);
492 found_non_mark |= !(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK);
494 if (likely (c->buffer->info[skippy_iter.idx].codepoint != component[i])) return TRACE_RETURN (false);
497 unsigned int klass = first_was_mark && found_non_mark ? HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE : 0;
499 /* Allocate new ligature id */
500 unsigned int lig_id = allocate_lig_id (c->buffer);
501 set_lig_props (c->buffer->cur(), lig_id, 0);
503 if (skippy_iter.idx < c->buffer->idx + count) /* No input glyphs skipped */
505 c->replace_glyphs_be16 (count, 1, (const uint16_t *) &ligGlyph, klass);
509 c->replace_glyph (ligGlyph);
511 /* Now we must do a second loop to copy the skipped glyphs to
512 `out' and assign component values to it. We start with the
513 glyph after the first component. Glyphs between component
514 i and i+1 belong to component i. Together with the lig_id
515 value it is later possible to check whether a specific
516 component value really belongs to a given ligature. */
518 for (unsigned int i = 1; i < count; i++)
520 while (c->should_mark_skip_current_glyph ())
522 set_lig_props (c->buffer->cur(), lig_id, i);
523 c->replace_glyph (c->buffer->cur().codepoint);
526 /* Skip the base glyph */
531 return TRACE_RETURN (true);
535 inline bool sanitize (hb_sanitize_context_t *c) {
537 return TRACE_RETURN (ligGlyph.sanitize (c) && component.sanitize (c));
541 GlyphID ligGlyph; /* GlyphID of ligature to substitute */
542 HeadlessArrayOf<GlyphID>
543 component; /* Array of component GlyphIDs--start
544 * with the second component--ordered
545 * in writing direction */
547 DEFINE_SIZE_ARRAY (4, component);
552 friend struct LigatureSubstFormat1;
556 inline void closure (hb_closure_context_t *c) const
559 unsigned int num_ligs = ligature.len;
560 for (unsigned int i = 0; i < num_ligs; i++)
561 (this+ligature[i]).closure (c);
564 inline bool would_apply (hb_codepoint_t second) const
566 unsigned int num_ligs = ligature.len;
567 for (unsigned int i = 0; i < num_ligs; i++)
569 const Ligature &lig = this+ligature[i];
570 if (lig.would_apply (second))
576 inline bool apply (hb_apply_context_t *c) const
579 unsigned int num_ligs = ligature.len;
580 for (unsigned int i = 0; i < num_ligs; i++)
582 const Ligature &lig = this+ligature[i];
583 if (lig.apply (c)) return TRACE_RETURN (true);
586 return TRACE_RETURN (false);
590 inline bool sanitize (hb_sanitize_context_t *c) {
592 return TRACE_RETURN (ligature.sanitize (c, this));
596 OffsetArrayOf<Ligature>
597 ligature; /* Array LigatureSet tables
598 * ordered by preference */
600 DEFINE_SIZE_ARRAY (2, ligature);
603 struct LigatureSubstFormat1
605 friend struct LigatureSubst;
609 inline void closure (hb_closure_context_t *c) const
613 for (iter.init (this+coverage); iter.more (); iter.next ()) {
614 if (c->glyphs->has (iter.get_glyph ()))
615 (this+ligatureSet[iter.get_coverage ()]).closure (c);
619 inline bool would_apply (hb_codepoint_t first, hb_codepoint_t second) const
622 return (index = (this+coverage) (first)) != NOT_COVERED &&
623 (this+ligatureSet[index]).would_apply (second);
626 inline bool apply (hb_apply_context_t *c) const
629 hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
631 unsigned int index = (this+coverage) (glyph_id);
632 if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
634 const LigatureSet &lig_set = this+ligatureSet[index];
635 return TRACE_RETURN (lig_set.apply (c));
638 inline bool sanitize (hb_sanitize_context_t *c) {
640 return TRACE_RETURN (coverage.sanitize (c, this) && ligatureSet.sanitize (c, this));
644 USHORT format; /* Format identifier--format = 1 */
646 coverage; /* Offset to Coverage table--from
647 * beginning of Substitution table */
648 OffsetArrayOf<LigatureSet>
649 ligatureSet; /* Array LigatureSet tables
650 * ordered by Coverage Index */
652 DEFINE_SIZE_ARRAY (6, ligatureSet);
657 friend struct SubstLookupSubTable;
661 inline void closure (hb_closure_context_t *c) const
665 case 1: u.format1.closure (c); break;
670 inline bool would_apply (hb_codepoint_t first, hb_codepoint_t second) const
673 case 1: return u.format1.would_apply (first, second);
674 default:return false;
678 inline bool apply (hb_apply_context_t *c) const
682 case 1: return TRACE_RETURN (u.format1.apply (c));
683 default:return TRACE_RETURN (false);
687 inline bool sanitize (hb_sanitize_context_t *c) {
689 if (!u.format.sanitize (c)) return TRACE_RETURN (false);
691 case 1: return TRACE_RETURN (u.format1.sanitize (c));
692 default:return TRACE_RETURN (true);
698 USHORT format; /* Format identifier */
699 LigatureSubstFormat1 format1;
704 static inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index);
705 static inline void closure_lookup (hb_closure_context_t *c, unsigned int lookup_index);
707 struct ContextSubst : Context
709 friend struct SubstLookupSubTable;
713 inline void closure (hb_closure_context_t *c) const
716 return Context::closure (c, closure_lookup);
719 inline bool apply (hb_apply_context_t *c) const
722 return TRACE_RETURN (Context::apply (c, substitute_lookup));
726 struct ChainContextSubst : ChainContext
728 friend struct SubstLookupSubTable;
732 inline void closure (hb_closure_context_t *c) const
735 return ChainContext::closure (c, closure_lookup);
738 inline bool apply (hb_apply_context_t *c) const
741 return TRACE_RETURN (ChainContext::apply (c, substitute_lookup));
746 struct ExtensionSubst : Extension
748 friend struct SubstLookupSubTable;
749 friend struct SubstLookup;
752 inline const struct SubstLookupSubTable& get_subtable (void) const
754 unsigned int offset = get_offset ();
755 if (unlikely (!offset)) return Null(SubstLookupSubTable);
756 return StructAtOffset<SubstLookupSubTable> (this, offset);
759 inline void closure (hb_closure_context_t *c) const;
760 inline bool would_apply (hb_codepoint_t glyph_id) const;
761 inline bool would_apply (hb_codepoint_t first, hb_codepoint_t second) const;
763 inline bool apply (hb_apply_context_t *c) const;
765 inline bool sanitize (hb_sanitize_context_t *c);
767 inline bool is_reverse (void) const;
771 struct ReverseChainSingleSubstFormat1
773 friend struct ReverseChainSingleSubst;
777 inline void closure (hb_closure_context_t *c) const
780 const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
784 count = backtrack.len;
785 for (unsigned int i = 0; i < count; i++)
786 if (!(this+backtrack[i]).intersects (c->glyphs))
789 count = lookahead.len;
790 for (unsigned int i = 0; i < count; i++)
791 if (!(this+lookahead[i]).intersects (c->glyphs))
794 const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
796 for (iter.init (this+coverage); iter.more (); iter.next ()) {
797 if (c->glyphs->has (iter.get_glyph ()))
798 c->glyphs->add (substitute[iter.get_coverage ()]);
802 inline bool apply (hb_apply_context_t *c) const
805 if (unlikely (c->nesting_level_left != MAX_NESTING_LEVEL))
806 return TRACE_RETURN (false); /* No chaining to this type */
808 unsigned int index = (this+coverage) (c->buffer->cur().codepoint);
809 if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
811 const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
812 const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
814 if (match_backtrack (c,
815 backtrack.len, (USHORT *) backtrack.array,
816 match_coverage, this) &&
818 lookahead.len, (USHORT *) lookahead.array,
819 match_coverage, this,
822 c->buffer->cur().codepoint = substitute[index];
823 c->buffer->idx--; /* Reverse! */
824 return TRACE_RETURN (true);
827 return TRACE_RETURN (false);
830 inline bool sanitize (hb_sanitize_context_t *c) {
832 if (!(coverage.sanitize (c, this) && backtrack.sanitize (c, this)))
833 return TRACE_RETURN (false);
834 OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
835 if (!lookahead.sanitize (c, this))
836 return TRACE_RETURN (false);
837 ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
838 return TRACE_RETURN (substitute.sanitize (c));
842 USHORT format; /* Format identifier--format = 1 */
844 coverage; /* Offset to Coverage table--from
845 * beginning of table */
846 OffsetArrayOf<Coverage>
847 backtrack; /* Array of coverage tables
848 * in backtracking sequence, in glyph
850 OffsetArrayOf<Coverage>
851 lookaheadX; /* Array of coverage tables
852 * in lookahead sequence, in glyph
855 substituteX; /* Array of substitute
856 * GlyphIDs--ordered by Coverage Index */
858 DEFINE_SIZE_MIN (10);
861 struct ReverseChainSingleSubst
863 friend struct SubstLookupSubTable;
867 inline void closure (hb_closure_context_t *c) const
871 case 1: u.format1.closure (c); break;
876 inline bool apply (hb_apply_context_t *c) const
880 case 1: return TRACE_RETURN (u.format1.apply (c));
881 default:return TRACE_RETURN (false);
885 inline bool sanitize (hb_sanitize_context_t *c) {
887 if (!u.format.sanitize (c)) return TRACE_RETURN (false);
889 case 1: return TRACE_RETURN (u.format1.sanitize (c));
890 default:return TRACE_RETURN (true);
896 USHORT format; /* Format identifier */
897 ReverseChainSingleSubstFormat1 format1;
907 struct SubstLookupSubTable
909 friend struct SubstLookup;
919 ReverseChainSingle = 8
922 inline void closure (hb_closure_context_t *c,
923 unsigned int lookup_type) const
926 switch (lookup_type) {
927 case Single: u.single.closure (c); break;
928 case Multiple: u.multiple.closure (c); break;
929 case Alternate: u.alternate.closure (c); break;
930 case Ligature: u.ligature.closure (c); break;
931 case Context: u.c.closure (c); break;
932 case ChainContext: u.chainContext.closure (c); break;
933 case Extension: u.extension.closure (c); break;
934 case ReverseChainSingle: u.reverseChainContextSingle.closure (c); break;
939 inline bool would_apply (hb_codepoint_t glyph_id,
940 unsigned int lookup_type) const
942 switch (lookup_type) {
943 case Single: return u.single.would_apply (glyph_id);
944 case Multiple: return u.multiple.would_apply (glyph_id);
945 case Alternate: return u.alternate.would_apply (glyph_id);
946 case Extension: return u.extension.would_apply (glyph_id);
947 default: return false;
950 inline bool would_apply (hb_codepoint_t first,
951 hb_codepoint_t second,
952 unsigned int lookup_type) const
954 switch (lookup_type) {
955 case Ligature: return u.ligature.would_apply (first, second);
956 case Extension: return u.extension.would_apply (first, second);
957 default: return false;
961 inline bool apply (hb_apply_context_t *c, unsigned int lookup_type) const
964 switch (lookup_type) {
965 case Single: return TRACE_RETURN (u.single.apply (c));
966 case Multiple: return TRACE_RETURN (u.multiple.apply (c));
967 case Alternate: return TRACE_RETURN (u.alternate.apply (c));
968 case Ligature: return TRACE_RETURN (u.ligature.apply (c));
969 case Context: return TRACE_RETURN (u.c.apply (c));
970 case ChainContext: return TRACE_RETURN (u.chainContext.apply (c));
971 case Extension: return TRACE_RETURN (u.extension.apply (c));
972 case ReverseChainSingle: return TRACE_RETURN (u.reverseChainContextSingle.apply (c));
973 default: return TRACE_RETURN (false);
977 inline bool sanitize (hb_sanitize_context_t *c, unsigned int lookup_type) {
979 switch (lookup_type) {
980 case Single: return TRACE_RETURN (u.single.sanitize (c));
981 case Multiple: return TRACE_RETURN (u.multiple.sanitize (c));
982 case Alternate: return TRACE_RETURN (u.alternate.sanitize (c));
983 case Ligature: return TRACE_RETURN (u.ligature.sanitize (c));
984 case Context: return TRACE_RETURN (u.c.sanitize (c));
985 case ChainContext: return TRACE_RETURN (u.chainContext.sanitize (c));
986 case Extension: return TRACE_RETURN (u.extension.sanitize (c));
987 case ReverseChainSingle: return TRACE_RETURN (u.reverseChainContextSingle.sanitize (c));
988 default: return TRACE_RETURN (true);
996 MultipleSubst multiple;
997 AlternateSubst alternate;
998 LigatureSubst ligature;
1000 ChainContextSubst chainContext;
1001 ExtensionSubst extension;
1002 ReverseChainSingleSubst reverseChainContextSingle;
1005 DEFINE_SIZE_UNION (2, sub_format);
1009 struct SubstLookup : Lookup
1011 inline const SubstLookupSubTable& get_subtable (unsigned int i) const
1012 { return this+CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable)[i]; }
1014 inline static bool lookup_type_is_reverse (unsigned int lookup_type)
1015 { return lookup_type == SubstLookupSubTable::ReverseChainSingle; }
1017 inline bool is_reverse (void) const
1019 unsigned int type = get_type ();
1020 if (unlikely (type == SubstLookupSubTable::Extension))
1021 return CastR<ExtensionSubst> (get_subtable(0)).is_reverse ();
1022 return lookup_type_is_reverse (type);
1025 inline void closure (hb_closure_context_t *c) const
1027 unsigned int lookup_type = get_type ();
1028 unsigned int count = get_subtable_count ();
1029 for (unsigned int i = 0; i < count; i++)
1030 get_subtable (i).closure (c, lookup_type);
1033 inline bool would_apply (hb_codepoint_t glyph_id) const
1035 unsigned int lookup_type = get_type ();
1036 unsigned int count = get_subtable_count ();
1037 for (unsigned int i = 0; i < count; i++)
1038 if (get_subtable (i).would_apply (glyph_id, lookup_type))
1042 inline bool would_apply (hb_codepoint_t first, hb_codepoint_t second) const
1044 unsigned int lookup_type = get_type ();
1045 unsigned int count = get_subtable_count ();
1046 for (unsigned int i = 0; i < count; i++)
1047 if (get_subtable (i).would_apply (first, second, lookup_type))
1052 inline bool apply_once (hb_apply_context_t *c) const
1054 unsigned int lookup_type = get_type ();
1056 if (!_hb_ot_layout_check_glyph_property (c->face, &c->buffer->cur(), c->lookup_props, &c->property))
1059 if (unlikely (lookup_type == SubstLookupSubTable::Extension))
1061 /* The spec says all subtables should have the same type.
1062 * This is specially important if one has a reverse type!
1064 * This is rather slow to do this here for every glyph,
1065 * but it's easiest, and who uses extension lookups anyway?!*/
1066 unsigned int type = get_subtable(0).u.extension.get_type ();
1067 unsigned int count = get_subtable_count ();
1068 for (unsigned int i = 1; i < count; i++)
1069 if (get_subtable(i).u.extension.get_type () != type)
1073 unsigned int count = get_subtable_count ();
1074 for (unsigned int i = 0; i < count; i++)
1075 if (get_subtable (i).apply (c, lookup_type))
1081 inline bool apply_string (hb_apply_context_t *c) const
1085 if (unlikely (!c->buffer->len))
1088 c->set_lookup (*this);
1090 if (likely (!is_reverse ()))
1092 /* in/out forward substitution */
1093 c->buffer->clear_output ();
1095 while (c->buffer->idx < c->buffer->len)
1097 if ((c->buffer->cur().mask & c->lookup_mask) && apply_once (c))
1100 c->buffer->next_glyph ();
1104 c->buffer->swap_buffers ();
1108 /* in-place backward substitution */
1109 c->buffer->idx = c->buffer->len - 1;
1112 if ((c->buffer->cur().mask & c->lookup_mask) && apply_once (c))
1118 while ((int) c->buffer->idx >= 0);
1124 inline bool sanitize (hb_sanitize_context_t *c) {
1126 if (unlikely (!Lookup::sanitize (c))) return TRACE_RETURN (false);
1127 OffsetArrayOf<SubstLookupSubTable> &list = CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable);
1128 return TRACE_RETURN (list.sanitize (c, this, get_type ()));
1132 typedef OffsetListOf<SubstLookup> SubstLookupList;
1135 * GSUB -- The Glyph Substitution Table
1138 struct GSUB : GSUBGPOS
1140 static const hb_tag_t Tag = HB_OT_TAG_GSUB;
1142 inline const SubstLookup& get_lookup (unsigned int i) const
1143 { return CastR<SubstLookup> (GSUBGPOS::get_lookup (i)); }
1145 inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index) const
1146 { return get_lookup (lookup_index).apply_string (c); }
1148 static inline void substitute_start (hb_buffer_t *buffer);
1149 static inline void substitute_finish (hb_buffer_t *buffer);
1151 inline void closure_lookup (hb_closure_context_t *c,
1152 unsigned int lookup_index) const
1153 { return get_lookup (lookup_index).closure (c); }
1155 inline bool sanitize (hb_sanitize_context_t *c) {
1157 if (unlikely (!GSUBGPOS::sanitize (c))) return TRACE_RETURN (false);
1158 OffsetTo<SubstLookupList> &list = CastR<OffsetTo<SubstLookupList> > (lookupList);
1159 return TRACE_RETURN (list.sanitize (c, this));
1162 DEFINE_SIZE_STATIC (10);
1167 GSUB::substitute_start (hb_buffer_t *buffer)
1169 HB_BUFFER_ALLOCATE_VAR (buffer, props_cache);
1170 HB_BUFFER_ALLOCATE_VAR (buffer, lig_props);
1171 HB_BUFFER_ALLOCATE_VAR (buffer, syllable);
1173 unsigned int count = buffer->len;
1174 for (unsigned int i = 0; i < count; i++)
1175 buffer->info[i].props_cache() = buffer->info[i].lig_props() = buffer->info[i].syllable() = 0;
1179 GSUB::substitute_finish (hb_buffer_t *buffer HB_UNUSED)
1184 /* Out-of-class implementation for methods recursing */
1186 inline void ExtensionSubst::closure (hb_closure_context_t *c) const
1188 get_subtable ().closure (c, get_type ());
1191 inline bool ExtensionSubst::would_apply (hb_codepoint_t glyph_id) const
1193 return get_subtable ().would_apply (glyph_id, get_type ());
1196 inline bool ExtensionSubst::would_apply (hb_codepoint_t first, hb_codepoint_t second) const
1198 return get_subtable ().would_apply (first, second, get_type ());
1201 inline bool ExtensionSubst::apply (hb_apply_context_t *c) const
1204 return TRACE_RETURN (get_subtable ().apply (c, get_type ()));
1207 inline bool ExtensionSubst::sanitize (hb_sanitize_context_t *c)
1210 if (unlikely (!Extension::sanitize (c))) return TRACE_RETURN (false);
1211 unsigned int offset = get_offset ();
1212 if (unlikely (!offset)) return TRACE_RETURN (true);
1213 return TRACE_RETURN (StructAtOffset<SubstLookupSubTable> (this, offset).sanitize (c, get_type ()));
1216 inline bool ExtensionSubst::is_reverse (void) const
1218 unsigned int type = get_type ();
1219 if (unlikely (type == SubstLookupSubTable::Extension))
1220 return CastR<ExtensionSubst> (get_subtable()).is_reverse ();
1221 return SubstLookup::lookup_type_is_reverse (type);
1224 static inline void closure_lookup (hb_closure_context_t *c, unsigned int lookup_index)
1226 const GSUB &gsub = *(c->face->ot_layout->gsub);
1227 const SubstLookup &l = gsub.get_lookup (lookup_index);
1229 if (unlikely (c->nesting_level_left == 0))
1232 c->nesting_level_left--;
1234 c->nesting_level_left++;
1237 static inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index)
1239 const GSUB &gsub = *(c->face->ot_layout->gsub);
1240 const SubstLookup &l = gsub.get_lookup (lookup_index);
1242 if (unlikely (c->nesting_level_left == 0))
1245 hb_apply_context_t new_c (*c);
1246 new_c.nesting_level_left--;
1247 new_c.set_lookup (l);
1248 return l.apply_once (&new_c);
1253 #endif /* HB_OT_LAYOUT_GSUB_TABLE_HH */