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