Fix scale issues
[profile/ivi/org.tizen.video-player.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
33 struct SingleSubstFormat1
34 {
35   friend struct SingleSubst;
36
37   private:
38
39   inline bool apply (hb_apply_context_t *c) const
40   {
41     TRACE_APPLY ();
42     hb_codepoint_t glyph_id = c->buffer->info[c->buffer->i].codepoint;
43     unsigned int index = (this+coverage) (glyph_id);
44     if (likely (index == NOT_COVERED))
45       return false;
46
47     glyph_id += deltaGlyphID;
48     c->buffer->replace_glyph (glyph_id);
49
50     /* We inherit the old glyph class to the substituted glyph */
51     if (_hb_ot_layout_has_new_glyph_classes (c->layout->face))
52       _hb_ot_layout_set_glyph_property (c->layout->face, glyph_id, c->property);
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->buffer->replace_glyph (glyph_id);
93
94     /* We inherit the old glyph class to the substituted glyph */
95     if (_hb_ot_layout_has_new_glyph_classes (c->layout->face))
96       _hb_ot_layout_set_glyph_property (c->layout->face, glyph_id, c->property);
97
98     return true;
99   }
100
101   inline bool sanitize (hb_sanitize_context_t *c) {
102     TRACE_SANITIZE ();
103     return coverage.sanitize (c, this)
104         && substitute.sanitize (c);
105   }
106
107   private:
108   USHORT        format;                 /* Format identifier--format = 2 */
109   OffsetTo<Coverage>
110                 coverage;               /* Offset to Coverage table--from
111                                          * beginning of Substitution table */
112   ArrayOf<GlyphID>
113                 substitute;             /* Array of substitute
114                                          * GlyphIDs--ordered by Coverage Index */
115   public:
116   DEFINE_SIZE_ARRAY (6, substitute);
117 };
118
119 struct SingleSubst
120 {
121   friend struct SubstLookupSubTable;
122
123   private:
124
125   inline bool apply (hb_apply_context_t *c) const
126   {
127     TRACE_APPLY ();
128     switch (u.format) {
129     case 1: return u.format1.apply (c);
130     case 2: return u.format2.apply (c);
131     default:return false;
132     }
133   }
134
135   inline bool sanitize (hb_sanitize_context_t *c) {
136     TRACE_SANITIZE ();
137     if (!u.format.sanitize (c)) return false;
138     switch (u.format) {
139     case 1: return u.format1.sanitize (c);
140     case 2: return u.format2.sanitize (c);
141     default:return true;
142     }
143   }
144
145   private:
146   union {
147   USHORT                format;         /* Format identifier */
148   SingleSubstFormat1    format1;
149   SingleSubstFormat2    format2;
150   } u;
151 };
152
153
154 struct Sequence
155 {
156   friend struct MultipleSubstFormat1;
157
158   private:
159   inline bool apply (hb_apply_context_t *c) const
160   {
161     TRACE_APPLY ();
162     if (unlikely (!substitute.len))
163       return false;
164
165     c->buffer->add_output_glyphs_be16 (1,
166                                        substitute.len, (const uint16_t *) substitute.array,
167                                        0xFFFF, 0xFFFF);
168
169     /* This is a guess only ... */
170     if (_hb_ot_layout_has_new_glyph_classes (c->layout->face))
171     {
172       unsigned int property = c->property;
173       if (property == HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE)
174         property = HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH;
175
176       unsigned int count = substitute.len;
177       for (unsigned int n = 0; n < count; n++)
178         _hb_ot_layout_set_glyph_property (c->layout->face, substitute[n], property);
179     }
180
181     return true;
182   }
183
184   public:
185   inline bool sanitize (hb_sanitize_context_t *c) {
186     TRACE_SANITIZE ();
187     return substitute.sanitize (c);
188   }
189
190   private:
191   ArrayOf<GlyphID>
192                 substitute;             /* String of GlyphIDs to substitute */
193   public:
194   DEFINE_SIZE_ARRAY (2, substitute);
195 };
196
197 struct MultipleSubstFormat1
198 {
199   friend struct MultipleSubst;
200
201   private:
202
203   inline bool apply (hb_apply_context_t *c) const
204   {
205     TRACE_APPLY ();
206
207     unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoint);
208     if (likely (index == NOT_COVERED))
209       return false;
210
211     return (this+sequence[index]).apply (c);
212   }
213
214   inline bool sanitize (hb_sanitize_context_t *c) {
215     TRACE_SANITIZE ();
216     return coverage.sanitize (c, this)
217         && sequence.sanitize (c, this);
218   }
219
220   private:
221   USHORT        format;                 /* Format identifier--format = 1 */
222   OffsetTo<Coverage>
223                 coverage;               /* Offset to Coverage table--from
224                                          * beginning of Substitution table */
225   OffsetArrayOf<Sequence>
226                 sequence;               /* Array of Sequence tables
227                                          * ordered by Coverage Index */
228   public:
229   DEFINE_SIZE_ARRAY (6, sequence);
230 };
231
232 struct MultipleSubst
233 {
234   friend struct SubstLookupSubTable;
235
236   private:
237
238   inline bool apply (hb_apply_context_t *c) const
239   {
240     TRACE_APPLY ();
241     switch (u.format) {
242     case 1: return u.format1.apply (c);
243     default:return false;
244     }
245   }
246
247   inline bool sanitize (hb_sanitize_context_t *c) {
248     TRACE_SANITIZE ();
249     if (!u.format.sanitize (c)) return false;
250     switch (u.format) {
251     case 1: return u.format1.sanitize (c);
252     default:return true;
253     }
254   }
255
256   private:
257   union {
258   USHORT                format;         /* Format identifier */
259   MultipleSubstFormat1  format1;
260   } u;
261 };
262
263
264 typedef ArrayOf<GlyphID> AlternateSet;  /* Array of alternate GlyphIDs--in
265                                          * arbitrary order */
266
267 struct AlternateSubstFormat1
268 {
269   friend struct AlternateSubst;
270
271   private:
272
273   inline bool apply (hb_apply_context_t *c) const
274   {
275     TRACE_APPLY ();
276     hb_codepoint_t glyph_id = c->buffer->info[c->buffer->i].codepoint;
277
278     unsigned int index = (this+coverage) (glyph_id);
279     if (likely (index == NOT_COVERED))
280       return false;
281
282     const AlternateSet &alt_set = this+alternateSet[index];
283
284     if (unlikely (!alt_set.len))
285       return false;
286
287     unsigned int alt_index = 0;
288
289     /* XXX callback to user to choose alternate
290     if (c->layout->face->altfunc)
291       alt_index = (c->layout->face->altfunc)(c->layout->layout, c->buffer,
292                                     c->buffer->out_len, glyph_id,
293                                     alt_set.len, alt_set.array);
294                                    */
295
296     if (unlikely (alt_index >= alt_set.len))
297       return false;
298
299     glyph_id = alt_set[alt_index];
300
301     c->buffer->replace_glyph (glyph_id);
302
303     /* We inherit the old glyph class to the substituted glyph */
304     if (_hb_ot_layout_has_new_glyph_classes (c->layout->face))
305       _hb_ot_layout_set_glyph_property (c->layout->face, glyph_id, c->property);
306
307     return true;
308   }
309
310   inline bool sanitize (hb_sanitize_context_t *c) {
311     TRACE_SANITIZE ();
312     return coverage.sanitize (c, this)
313         && alternateSet.sanitize (c, this);
314   }
315
316   private:
317   USHORT        format;                 /* Format identifier--format = 1 */
318   OffsetTo<Coverage>
319                 coverage;               /* Offset to Coverage table--from
320                                          * beginning of Substitution table */
321   OffsetArrayOf<AlternateSet>
322                 alternateSet;           /* Array of AlternateSet tables
323                                          * ordered by Coverage Index */
324   public:
325   DEFINE_SIZE_ARRAY (6, alternateSet);
326 };
327
328 struct AlternateSubst
329 {
330   friend struct SubstLookupSubTable;
331
332   private:
333
334   inline bool apply (hb_apply_context_t *c) const
335   {
336     TRACE_APPLY ();
337     switch (u.format) {
338     case 1: return u.format1.apply (c);
339     default:return false;
340     }
341   }
342
343   inline bool sanitize (hb_sanitize_context_t *c) {
344     TRACE_SANITIZE ();
345     if (!u.format.sanitize (c)) return false;
346     switch (u.format) {
347     case 1: return u.format1.sanitize (c);
348     default:return true;
349     }
350   }
351
352   private:
353   union {
354   USHORT                format;         /* Format identifier */
355   AlternateSubstFormat1 format1;
356   } u;
357 };
358
359
360 struct Ligature
361 {
362   friend struct LigatureSet;
363
364   private:
365   inline bool apply (hb_apply_context_t *c, bool is_mark) const
366   {
367     TRACE_APPLY ();
368     unsigned int i, j;
369     unsigned int count = component.len;
370     unsigned int end = MIN (c->buffer->len, c->buffer->i + c->context_length);
371     if (unlikely (c->buffer->i + count > end))
372       return false;
373
374     for (i = 1, j = c->buffer->i + 1; i < count; i++, j++)
375     {
376       unsigned int property;
377       while (_hb_ot_layout_skip_mark (c->layout->face, &c->buffer->info[j], c->lookup_flag, &property))
378       {
379         if (unlikely (j + count - i == end))
380           return false;
381         j++;
382       }
383
384       if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK))
385         is_mark = false;
386
387       if (likely (c->buffer->info[j].codepoint != component[i]))
388         return false;
389     }
390     /* This is just a guess ... */
391     if (_hb_ot_layout_has_new_glyph_classes (c->layout->face))
392       _hb_ot_layout_set_glyph_class (c->layout->face, ligGlyph,
393                                      is_mark ? HB_OT_LAYOUT_GLYPH_CLASS_MARK
394                                              : HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE);
395
396     if (j == c->buffer->i + i) /* No input glyphs skipped */
397       /* We don't use a new ligature ID if there are no skipped
398          glyphs and the ligature already has an ID. */
399       c->buffer->add_output_glyphs_be16 (i,
400                                          1, (const uint16_t *) &ligGlyph,
401                                          0,
402                                          c->buffer->info[c->buffer->i].lig_id && !c->buffer->info[c->buffer->i].component ?
403                                          0xFFFF : c->buffer->allocate_lig_id ());
404     else
405     {
406       unsigned int lig_id = c->buffer->allocate_lig_id ();
407       c->buffer->add_output_glyph (ligGlyph, 0xFFFF, lig_id);
408
409       /* Now we must do a second loop to copy the skipped glyphs to
410          `out' and assign component values to it.  We start with the
411          glyph after the first component.  Glyphs between component
412          i and i+1 belong to component i.  Together with the lig_id
413          value it is later possible to check whether a specific
414          component value really belongs to a given ligature. */
415
416       for ( i = 1; i < count; i++ )
417       {
418         while (_hb_ot_layout_skip_mark (c->layout->face, &c->buffer->info[c->buffer->i], c->lookup_flag, NULL))
419           c->buffer->add_output_glyph (c->buffer->info[c->buffer->i].codepoint, i, lig_id);
420
421         (c->buffer->i)++;
422       }
423     }
424
425     return true;
426   }
427
428   public:
429   inline bool sanitize (hb_sanitize_context_t *c) {
430     TRACE_SANITIZE ();
431     return ligGlyph.sanitize (c)
432         && component.sanitize (c);
433   }
434
435   private:
436   GlyphID       ligGlyph;               /* GlyphID of ligature to substitute */
437   HeadlessArrayOf<GlyphID>
438                 component;              /* Array of component GlyphIDs--start
439                                          * with the second  component--ordered
440                                          * in writing direction */
441   public:
442   DEFINE_SIZE_ARRAY (4, component);
443 };
444
445 struct LigatureSet
446 {
447   friend struct LigatureSubstFormat1;
448
449   private:
450   inline bool apply (hb_apply_context_t *c, bool is_mark) const
451   {
452     TRACE_APPLY ();
453     unsigned int num_ligs = ligature.len;
454     for (unsigned int i = 0; i < num_ligs; i++)
455     {
456       const Ligature &lig = this+ligature[i];
457       if (lig.apply (c, is_mark))
458         return true;
459     }
460
461     return false;
462   }
463
464   public:
465   inline bool sanitize (hb_sanitize_context_t *c) {
466     TRACE_SANITIZE ();
467     return ligature.sanitize (c, this);
468   }
469
470   private:
471   OffsetArrayOf<Ligature>
472                 ligature;               /* Array LigatureSet tables
473                                          * ordered by preference */
474   public:
475   DEFINE_SIZE_ARRAY (2, ligature);
476 };
477
478 struct LigatureSubstFormat1
479 {
480   friend struct LigatureSubst;
481
482   private:
483   inline bool apply (hb_apply_context_t *c) const
484   {
485     TRACE_APPLY ();
486     hb_codepoint_t glyph_id = c->buffer->info[c->buffer->i].codepoint;
487
488     bool first_is_mark = !!(c->property & HB_OT_LAYOUT_GLYPH_CLASS_MARK);
489
490     unsigned int index = (this+coverage) (glyph_id);
491     if (likely (index == NOT_COVERED))
492       return false;
493
494     const LigatureSet &lig_set = this+ligatureSet[index];
495     return lig_set.apply (c, first_is_mark);
496   }
497
498   inline bool sanitize (hb_sanitize_context_t *c) {
499     TRACE_SANITIZE ();
500     return coverage.sanitize (c, this)
501         && ligatureSet.sanitize (c, this);
502   }
503
504   private:
505   USHORT        format;                 /* Format identifier--format = 1 */
506   OffsetTo<Coverage>
507                 coverage;               /* Offset to Coverage table--from
508                                          * beginning of Substitution table */
509   OffsetArrayOf<LigatureSet>
510                 ligatureSet;            /* Array LigatureSet tables
511                                          * ordered by Coverage Index */
512   public:
513   DEFINE_SIZE_ARRAY (6, ligatureSet);
514 };
515
516 struct LigatureSubst
517 {
518   friend struct SubstLookupSubTable;
519
520   private:
521   inline bool apply (hb_apply_context_t *c) const
522   {
523     TRACE_APPLY ();
524     switch (u.format) {
525     case 1: return u.format1.apply (c);
526     default:return false;
527     }
528   }
529
530   inline bool sanitize (hb_sanitize_context_t *c) {
531     TRACE_SANITIZE ();
532     if (!u.format.sanitize (c)) return false;
533     switch (u.format) {
534     case 1: return u.format1.sanitize (c);
535     default:return true;
536     }
537   }
538
539   private:
540   union {
541   USHORT                format;         /* Format identifier */
542   LigatureSubstFormat1  format1;
543   } u;
544 };
545
546
547
548 static inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index);
549
550 struct ContextSubst : Context
551 {
552   friend struct SubstLookupSubTable;
553
554   private:
555   inline bool apply (hb_apply_context_t *c) const
556   {
557     TRACE_APPLY ();
558     return Context::apply (c, substitute_lookup);
559   }
560 };
561
562 struct ChainContextSubst : ChainContext
563 {
564   friend struct SubstLookupSubTable;
565
566   private:
567   inline bool apply (hb_apply_context_t *c) const
568   {
569     TRACE_APPLY ();
570     return ChainContext::apply (c, substitute_lookup);
571   }
572 };
573
574
575 struct ExtensionSubst : Extension
576 {
577   friend struct SubstLookupSubTable;
578   friend struct SubstLookup;
579
580   private:
581   inline const struct SubstLookupSubTable& get_subtable (void) const
582   {
583     unsigned int offset = get_offset ();
584     if (unlikely (!offset)) return Null(SubstLookupSubTable);
585     return StructAtOffset<SubstLookupSubTable> (this, offset);
586   }
587
588   inline bool apply (hb_apply_context_t *c) const;
589
590   inline bool sanitize (hb_sanitize_context_t *c);
591
592   inline bool is_reverse (void) const;
593 };
594
595
596 struct ReverseChainSingleSubstFormat1
597 {
598   friend struct ReverseChainSingleSubst;
599
600   private:
601   inline bool apply (hb_apply_context_t *c) const
602   {
603     TRACE_APPLY ();
604     if (unlikely (c->context_length != NO_CONTEXT))
605       return false; /* No chaining to this type */
606
607     unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoint);
608     if (likely (index == NOT_COVERED))
609       return false;
610
611     const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
612     const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
613
614     if (match_backtrack (c,
615                          backtrack.len, (USHORT *) backtrack.array,
616                          match_coverage, this) &&
617         match_lookahead (c,
618                          lookahead.len, (USHORT *) lookahead.array,
619                          match_coverage, this,
620                          1))
621     {
622       c->buffer->info[c->buffer->i].codepoint = substitute[index];
623       c->buffer->i--; /* Reverse! */
624       return true;
625     }
626
627     return false;
628   }
629
630   inline bool sanitize (hb_sanitize_context_t *c) {
631     TRACE_SANITIZE ();
632     if (!(coverage.sanitize (c, this)
633        && backtrack.sanitize (c, this)))
634       return false;
635     OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
636     if (!lookahead.sanitize (c, this))
637       return false;
638     ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
639     return substitute.sanitize (c);
640   }
641
642   private:
643   USHORT        format;                 /* Format identifier--format = 1 */
644   OffsetTo<Coverage>
645                 coverage;               /* Offset to Coverage table--from
646                                          * beginning of table */
647   OffsetArrayOf<Coverage>
648                 backtrack;              /* Array of coverage tables
649                                          * in backtracking sequence, in  glyph
650                                          * sequence order */
651   OffsetArrayOf<Coverage>
652                 lookaheadX;             /* Array of coverage tables
653                                          * in lookahead sequence, in glyph
654                                          * sequence order */
655   ArrayOf<GlyphID>
656                 substituteX;            /* Array of substitute
657                                          * GlyphIDs--ordered by Coverage Index */
658   public:
659   DEFINE_SIZE_MIN (10);
660 };
661
662 struct ReverseChainSingleSubst
663 {
664   friend struct SubstLookupSubTable;
665
666   private:
667   inline bool apply (hb_apply_context_t *c) const
668   {
669     TRACE_APPLY ();
670     switch (u.format) {
671     case 1: return u.format1.apply (c);
672     default:return false;
673     }
674   }
675
676   inline bool sanitize (hb_sanitize_context_t *c) {
677     TRACE_SANITIZE ();
678     if (!u.format.sanitize (c)) return false;
679     switch (u.format) {
680     case 1: return u.format1.sanitize (c);
681     default:return true;
682     }
683   }
684
685   private:
686   union {
687   USHORT                                format;         /* Format identifier */
688   ReverseChainSingleSubstFormat1        format1;
689   } u;
690 };
691
692
693
694 /*
695  * SubstLookup
696  */
697
698 struct SubstLookupSubTable
699 {
700   friend struct SubstLookup;
701
702   enum {
703     Single              = 1,
704     Multiple            = 2,
705     Alternate           = 3,
706     Ligature            = 4,
707     Context             = 5,
708     ChainContext        = 6,
709     Extension           = 7,
710     ReverseChainSingle  = 8
711   };
712
713   inline bool apply (hb_apply_context_t *c, unsigned int lookup_type) const
714   {
715     TRACE_APPLY ();
716     switch (lookup_type) {
717     case Single:                return u.single.apply (c);
718     case Multiple:              return u.multiple.apply (c);
719     case Alternate:             return u.alternate.apply (c);
720     case Ligature:              return u.ligature.apply (c);
721     case Context:               return u.c.apply (c);
722     case ChainContext:          return u.chainContext.apply (c);
723     case Extension:             return u.extension.apply (c);
724     case ReverseChainSingle:    return u.reverseChainContextSingle.apply (c);
725     default:return false;
726     }
727   }
728
729   inline bool sanitize (hb_sanitize_context_t *c, unsigned int lookup_type) {
730     TRACE_SANITIZE ();
731     switch (lookup_type) {
732     case Single:                return u.single.sanitize (c);
733     case Multiple:              return u.multiple.sanitize (c);
734     case Alternate:             return u.alternate.sanitize (c);
735     case Ligature:              return u.ligature.sanitize (c);
736     case Context:               return u.c.sanitize (c);
737     case ChainContext:          return u.chainContext.sanitize (c);
738     case Extension:             return u.extension.sanitize (c);
739     case ReverseChainSingle:    return u.reverseChainContextSingle.sanitize (c);
740     default:return true;
741     }
742   }
743
744   private:
745   union {
746   USHORT                        sub_format;
747   SingleSubst                   single;
748   MultipleSubst                 multiple;
749   AlternateSubst                alternate;
750   LigatureSubst                 ligature;
751   ContextSubst                  c;
752   ChainContextSubst             chainContext;
753   ExtensionSubst                extension;
754   ReverseChainSingleSubst       reverseChainContextSingle;
755   } u;
756   public:
757   DEFINE_SIZE_UNION (2, sub_format);
758 };
759
760
761 struct SubstLookup : Lookup
762 {
763   inline const SubstLookupSubTable& get_subtable (unsigned int i) const
764   { return this+CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable)[i]; }
765
766   inline static bool lookup_type_is_reverse (unsigned int lookup_type)
767   { return lookup_type == SubstLookupSubTable::ReverseChainSingle; }
768
769   inline bool is_reverse (void) const
770   {
771     unsigned int type = get_type ();
772     if (unlikely (type == SubstLookupSubTable::Extension))
773       return CastR<ExtensionSubst> (get_subtable(0)).is_reverse ();
774     return lookup_type_is_reverse (type);
775   }
776
777
778   inline bool apply_once (hb_ot_layout_context_t *layout,
779                           hb_buffer_t *buffer,
780                           unsigned int context_length,
781                           unsigned int nesting_level_left) const
782   {
783     unsigned int lookup_type = get_type ();
784     hb_apply_context_t c[1] = {{0}};
785
786     c->layout = layout;
787     c->buffer = buffer;
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, 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, 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->context_length, c->nesting_level_left - 1);
937 }
938
939
940 #endif /* HB_OT_LAYOUT_GSUB_PRIVATE_HH */