Towards normalization
[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->i].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->i].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->i].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->i].codepoint;
261     hb_mask_t glyph_mask = c->buffer->info[c->buffer->i].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->i + c->context_length);
348     if (unlikely (count < 2 || c->buffer->i + 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->i + 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->i].lig_comp() = 0;
376     c->buffer->info[c->buffer->i].lig_id() = lig_id;
377
378     if (j == c->buffer->i + 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->i], c->lookup_props, NULL))
396         {
397           c->buffer->info[c->buffer->i].lig_comp() = i;
398           c->buffer->info[c->buffer->i].lig_id() = lig_id;
399           c->replace_glyph (c->buffer->info[c->buffer->i].codepoint);
400         }
401
402         /* Skip the base glyph */
403         c->buffer->i++;
404       }
405     }
406
407     return true;
408   }
409
410   inline uint16_t allocate_lig_id (hb_buffer_t *buffer) const {
411     uint16_t lig_id = buffer->next_serial ();
412     if (unlikely (!lig_id)) lig_id = buffer->next_serial (); /* in case of overflow */
413     return lig_id;
414   }
415
416   public:
417   inline bool sanitize (hb_sanitize_context_t *c) {
418     TRACE_SANITIZE ();
419     return ligGlyph.sanitize (c)
420         && component.sanitize (c);
421   }
422
423   private:
424   GlyphID       ligGlyph;               /* GlyphID of ligature to substitute */
425   HeadlessArrayOf<GlyphID>
426                 component;              /* Array of component GlyphIDs--start
427                                          * with the second  component--ordered
428                                          * in writing direction */
429   public:
430   DEFINE_SIZE_ARRAY (4, component);
431 };
432
433 struct LigatureSet
434 {
435   friend struct LigatureSubstFormat1;
436
437   private:
438   inline bool apply (hb_apply_context_t *c) const
439   {
440     TRACE_APPLY ();
441     unsigned int num_ligs = ligature.len;
442     for (unsigned int i = 0; i < num_ligs; i++)
443     {
444       const Ligature &lig = this+ligature[i];
445       if (lig.apply (c))
446         return true;
447     }
448
449     return false;
450   }
451
452   public:
453   inline bool sanitize (hb_sanitize_context_t *c) {
454     TRACE_SANITIZE ();
455     return ligature.sanitize (c, this);
456   }
457
458   private:
459   OffsetArrayOf<Ligature>
460                 ligature;               /* Array LigatureSet tables
461                                          * ordered by preference */
462   public:
463   DEFINE_SIZE_ARRAY (2, ligature);
464 };
465
466 struct LigatureSubstFormat1
467 {
468   friend struct LigatureSubst;
469
470   private:
471   inline bool apply (hb_apply_context_t *c) const
472   {
473     TRACE_APPLY ();
474     hb_codepoint_t glyph_id = c->buffer->info[c->buffer->i].codepoint;
475
476     unsigned int index = (this+coverage) (glyph_id);
477     if (likely (index == NOT_COVERED))
478       return false;
479
480     const LigatureSet &lig_set = this+ligatureSet[index];
481     return lig_set.apply (c);
482   }
483
484   inline bool sanitize (hb_sanitize_context_t *c) {
485     TRACE_SANITIZE ();
486     return coverage.sanitize (c, this)
487         && ligatureSet.sanitize (c, this);
488   }
489
490   private:
491   USHORT        format;                 /* Format identifier--format = 1 */
492   OffsetTo<Coverage>
493                 coverage;               /* Offset to Coverage table--from
494                                          * beginning of Substitution table */
495   OffsetArrayOf<LigatureSet>
496                 ligatureSet;            /* Array LigatureSet tables
497                                          * ordered by Coverage Index */
498   public:
499   DEFINE_SIZE_ARRAY (6, ligatureSet);
500 };
501
502 struct LigatureSubst
503 {
504   friend struct SubstLookupSubTable;
505
506   private:
507   inline bool apply (hb_apply_context_t *c) const
508   {
509     TRACE_APPLY ();
510     switch (u.format) {
511     case 1: return u.format1.apply (c);
512     default:return false;
513     }
514   }
515
516   inline bool sanitize (hb_sanitize_context_t *c) {
517     TRACE_SANITIZE ();
518     if (!u.format.sanitize (c)) return false;
519     switch (u.format) {
520     case 1: return u.format1.sanitize (c);
521     default:return true;
522     }
523   }
524
525   private:
526   union {
527   USHORT                format;         /* Format identifier */
528   LigatureSubstFormat1  format1;
529   } u;
530 };
531
532
533 HB_BEGIN_DECLS
534 static inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index);
535 HB_END_DECLS
536
537 struct ContextSubst : Context
538 {
539   friend struct SubstLookupSubTable;
540
541   private:
542   inline bool apply (hb_apply_context_t *c) const
543   {
544     TRACE_APPLY ();
545     return Context::apply (c, substitute_lookup);
546   }
547 };
548
549 struct ChainContextSubst : ChainContext
550 {
551   friend struct SubstLookupSubTable;
552
553   private:
554   inline bool apply (hb_apply_context_t *c) const
555   {
556     TRACE_APPLY ();
557     return ChainContext::apply (c, substitute_lookup);
558   }
559 };
560
561
562 struct ExtensionSubst : Extension
563 {
564   friend struct SubstLookupSubTable;
565   friend struct SubstLookup;
566
567   private:
568   inline const struct SubstLookupSubTable& get_subtable (void) const
569   {
570     unsigned int offset = get_offset ();
571     if (unlikely (!offset)) return Null(SubstLookupSubTable);
572     return StructAtOffset<SubstLookupSubTable> (this, offset);
573   }
574
575   inline bool apply (hb_apply_context_t *c) const;
576
577   inline bool sanitize (hb_sanitize_context_t *c);
578
579   inline bool is_reverse (void) const;
580 };
581
582
583 struct ReverseChainSingleSubstFormat1
584 {
585   friend struct ReverseChainSingleSubst;
586
587   private:
588   inline bool apply (hb_apply_context_t *c) const
589   {
590     TRACE_APPLY ();
591     if (unlikely (c->context_length != NO_CONTEXT))
592       return false; /* No chaining to this type */
593
594     unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoint);
595     if (likely (index == NOT_COVERED))
596       return false;
597
598     const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
599     const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
600
601     if (match_backtrack (c,
602                          backtrack.len, (USHORT *) backtrack.array,
603                          match_coverage, this) &&
604         match_lookahead (c,
605                          lookahead.len, (USHORT *) lookahead.array,
606                          match_coverage, this,
607                          1))
608     {
609       c->buffer->info[c->buffer->i].codepoint = substitute[index];
610       c->buffer->i--; /* Reverse! */
611       return true;
612     }
613
614     return false;
615   }
616
617   inline bool sanitize (hb_sanitize_context_t *c) {
618     TRACE_SANITIZE ();
619     if (!(coverage.sanitize (c, this)
620        && backtrack.sanitize (c, this)))
621       return false;
622     OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
623     if (!lookahead.sanitize (c, this))
624       return false;
625     ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
626     return substitute.sanitize (c);
627   }
628
629   private:
630   USHORT        format;                 /* Format identifier--format = 1 */
631   OffsetTo<Coverage>
632                 coverage;               /* Offset to Coverage table--from
633                                          * beginning of table */
634   OffsetArrayOf<Coverage>
635                 backtrack;              /* Array of coverage tables
636                                          * in backtracking sequence, in  glyph
637                                          * sequence order */
638   OffsetArrayOf<Coverage>
639                 lookaheadX;             /* Array of coverage tables
640                                          * in lookahead sequence, in glyph
641                                          * sequence order */
642   ArrayOf<GlyphID>
643                 substituteX;            /* Array of substitute
644                                          * GlyphIDs--ordered by Coverage Index */
645   public:
646   DEFINE_SIZE_MIN (10);
647 };
648
649 struct ReverseChainSingleSubst
650 {
651   friend struct SubstLookupSubTable;
652
653   private:
654   inline bool apply (hb_apply_context_t *c) const
655   {
656     TRACE_APPLY ();
657     switch (u.format) {
658     case 1: return u.format1.apply (c);
659     default:return false;
660     }
661   }
662
663   inline bool sanitize (hb_sanitize_context_t *c) {
664     TRACE_SANITIZE ();
665     if (!u.format.sanitize (c)) return false;
666     switch (u.format) {
667     case 1: return u.format1.sanitize (c);
668     default:return true;
669     }
670   }
671
672   private:
673   union {
674   USHORT                                format;         /* Format identifier */
675   ReverseChainSingleSubstFormat1        format1;
676   } u;
677 };
678
679
680
681 /*
682  * SubstLookup
683  */
684
685 struct SubstLookupSubTable
686 {
687   friend struct SubstLookup;
688
689   enum {
690     Single              = 1,
691     Multiple            = 2,
692     Alternate           = 3,
693     Ligature            = 4,
694     Context             = 5,
695     ChainContext        = 6,
696     Extension           = 7,
697     ReverseChainSingle  = 8
698   };
699
700   inline bool apply (hb_apply_context_t *c, unsigned int lookup_type) const
701   {
702     TRACE_APPLY ();
703     switch (lookup_type) {
704     case Single:                return u.single.apply (c);
705     case Multiple:              return u.multiple.apply (c);
706     case Alternate:             return u.alternate.apply (c);
707     case Ligature:              return u.ligature.apply (c);
708     case Context:               return u.c.apply (c);
709     case ChainContext:          return u.chainContext.apply (c);
710     case Extension:             return u.extension.apply (c);
711     case ReverseChainSingle:    return u.reverseChainContextSingle.apply (c);
712     default:return false;
713     }
714   }
715
716   inline bool sanitize (hb_sanitize_context_t *c, unsigned int lookup_type) {
717     TRACE_SANITIZE ();
718     switch (lookup_type) {
719     case Single:                return u.single.sanitize (c);
720     case Multiple:              return u.multiple.sanitize (c);
721     case Alternate:             return u.alternate.sanitize (c);
722     case Ligature:              return u.ligature.sanitize (c);
723     case Context:               return u.c.sanitize (c);
724     case ChainContext:          return u.chainContext.sanitize (c);
725     case Extension:             return u.extension.sanitize (c);
726     case ReverseChainSingle:    return u.reverseChainContextSingle.sanitize (c);
727     default:return true;
728     }
729   }
730
731   private:
732   union {
733   USHORT                        sub_format;
734   SingleSubst                   single;
735   MultipleSubst                 multiple;
736   AlternateSubst                alternate;
737   LigatureSubst                 ligature;
738   ContextSubst                  c;
739   ChainContextSubst             chainContext;
740   ExtensionSubst                extension;
741   ReverseChainSingleSubst       reverseChainContextSingle;
742   } u;
743   public:
744   DEFINE_SIZE_UNION (2, sub_format);
745 };
746
747
748 struct SubstLookup : Lookup
749 {
750   inline const SubstLookupSubTable& get_subtable (unsigned int i) const
751   { return this+CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable)[i]; }
752
753   inline static bool lookup_type_is_reverse (unsigned int lookup_type)
754   { return lookup_type == SubstLookupSubTable::ReverseChainSingle; }
755
756   inline bool is_reverse (void) const
757   {
758     unsigned int type = get_type ();
759     if (unlikely (type == SubstLookupSubTable::Extension))
760       return CastR<ExtensionSubst> (get_subtable(0)).is_reverse ();
761     return lookup_type_is_reverse (type);
762   }
763
764
765   inline bool apply_once (hb_face_t *face,
766                           hb_buffer_t *buffer,
767                           hb_mask_t lookup_mask,
768                           unsigned int context_length,
769                           unsigned int nesting_level_left) const
770   {
771     unsigned int lookup_type = get_type ();
772     hb_apply_context_t c[1] = {{0}};
773
774     c->face = face;
775     c->buffer = buffer;
776     c->direction = buffer->props.direction;
777     c->lookup_mask = lookup_mask;
778     c->context_length = context_length;
779     c->nesting_level_left = nesting_level_left;
780     c->lookup_props = get_props ();
781
782     if (!_hb_ot_layout_check_glyph_property (c->face, &c->buffer->info[c->buffer->i], c->lookup_props, &c->property))
783       return false;
784
785     if (unlikely (lookup_type == SubstLookupSubTable::Extension))
786     {
787       /* The spec says all subtables should have the same type.
788        * This is specially important if one has a reverse type!
789        *
790        * This is rather slow to do this here for every glyph,
791        * but it's easiest, and who uses extension lookups anyway?!*/
792       unsigned int count = get_subtable_count ();
793       unsigned int type = get_subtable(0).u.extension.get_type ();
794       for (unsigned int i = 1; i < count; i++)
795         if (get_subtable(i).u.extension.get_type () != type)
796           return false;
797     }
798
799     unsigned int count = get_subtable_count ();
800     for (unsigned int i = 0; i < count; i++)
801       if (get_subtable (i).apply (c, lookup_type))
802         return true;
803
804     return false;
805   }
806
807   inline bool apply_string (hb_face_t   *face,
808                             hb_buffer_t *buffer,
809                             hb_mask_t    mask) const
810   {
811     bool ret = false;
812
813     if (unlikely (!buffer->len))
814       return false;
815
816     if (likely (!is_reverse ()))
817     {
818         /* in/out forward substitution */
819         buffer->clear_output ();
820         buffer->i = 0;
821         while (buffer->i < buffer->len)
822         {
823           if ((buffer->info[buffer->i].mask & mask) &&
824               apply_once (face, buffer, mask, NO_CONTEXT, MAX_NESTING_LEVEL))
825             ret = true;
826           else
827             buffer->next_glyph ();
828
829         }
830         if (ret)
831           buffer->swap ();
832     }
833     else
834     {
835         /* in-place backward substitution */
836         buffer->i = buffer->len - 1;
837         do
838         {
839           if ((buffer->info[buffer->i].mask & mask) &&
840               apply_once (face, buffer, mask, NO_CONTEXT, MAX_NESTING_LEVEL))
841             ret = true;
842           else
843             buffer->i--;
844
845         }
846         while ((int) buffer->i >= 0);
847     }
848
849     return ret;
850   }
851
852   inline bool sanitize (hb_sanitize_context_t *c) {
853     TRACE_SANITIZE ();
854     if (unlikely (!Lookup::sanitize (c))) return false;
855     OffsetArrayOf<SubstLookupSubTable> &list = CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable);
856     return list.sanitize (c, this, get_type ());
857   }
858 };
859
860 typedef OffsetListOf<SubstLookup> SubstLookupList;
861
862 /*
863  * GSUB
864  */
865
866 struct GSUB : GSUBGPOS
867 {
868   static const hb_tag_t Tag     = HB_OT_TAG_GSUB;
869
870   inline const SubstLookup& get_lookup (unsigned int i) const
871   { return CastR<SubstLookup> (GSUBGPOS::get_lookup (i)); }
872
873   inline bool substitute_lookup (hb_face_t    *face,
874                                  hb_buffer_t  *buffer,
875                                  unsigned int  lookup_index,
876                                  hb_mask_t     mask) const
877   { return get_lookup (lookup_index).apply_string (face, buffer, mask); }
878
879   inline bool sanitize (hb_sanitize_context_t *c) {
880     TRACE_SANITIZE ();
881     if (unlikely (!GSUBGPOS::sanitize (c))) return false;
882     OffsetTo<SubstLookupList> &list = CastR<OffsetTo<SubstLookupList> > (lookupList);
883     return list.sanitize (c, this);
884   }
885   public:
886   DEFINE_SIZE_STATIC (10);
887 };
888
889
890 /* Out-of-class implementation for methods recursing */
891
892 inline bool ExtensionSubst::apply (hb_apply_context_t *c) const
893 {
894   TRACE_APPLY ();
895   return get_subtable ().apply (c, get_type ());
896 }
897
898 inline bool ExtensionSubst::sanitize (hb_sanitize_context_t *c)
899 {
900   TRACE_SANITIZE ();
901   if (unlikely (!Extension::sanitize (c))) return false;
902   unsigned int offset = get_offset ();
903   if (unlikely (!offset)) return true;
904   return StructAtOffset<SubstLookupSubTable> (this, offset).sanitize (c, get_type ());
905 }
906
907 inline bool ExtensionSubst::is_reverse (void) const
908 {
909   unsigned int type = get_type ();
910   if (unlikely (type == SubstLookupSubTable::Extension))
911     return CastR<ExtensionSubst> (get_subtable()).is_reverse ();
912   return SubstLookup::lookup_type_is_reverse (type);
913 }
914
915 static inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index)
916 {
917   const GSUB &gsub = *(c->face->ot_layout->gsub);
918   const SubstLookup &l = gsub.get_lookup (lookup_index);
919
920   if (unlikely (c->nesting_level_left == 0))
921     return false;
922
923   if (unlikely (c->context_length < 1))
924     return false;
925
926   return l.apply_once (c->face, c->buffer, c->lookup_mask, c->context_length, c->nesting_level_left - 1);
927 }
928
929
930 HB_END_DECLS
931
932 #endif /* HB_OT_LAYOUT_GSUB_PRIVATE_HH */