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