7539bc22fddb95e4db835aa04a583517a0916e41
[framework/uifw/harfbuzz.git] / src / hb-ot-layout-gsub-private.hh
1 /*
2  * Copyright (C) 2007,2008,2009  Red Hat, Inc.
3  *
4  *  This is part of HarfBuzz, an OpenType Layout engine 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 (APPLY_ARG_DEF) const
40   {
41     hb_codepoint_t glyph_id = IN_CURGLYPH ();
42     unsigned int index = (this+coverage) (glyph_id);
43     if (HB_LIKELY (index == NOT_COVERED))
44       return false;
45
46     glyph_id += deltaGlyphID;
47     _hb_buffer_replace_glyph (buffer, glyph_id);
48
49     /* We inherit the old glyph class to the substituted glyph */
50     if (_hb_ot_layout_has_new_glyph_classes (context->face))
51       _hb_ot_layout_set_glyph_property (context->face, glyph_id, property);
52
53     return true;
54   }
55
56   inline bool sanitize (SANITIZE_ARG_DEF) {
57     SANITIZE_DEBUG ();
58     return SANITIZE_THIS (coverage) && SANITIZE (deltaGlyphID);
59   }
60
61   private:
62   USHORT        format;                 /* Format identifier--format = 1 */
63   OffsetTo<Coverage>
64                 coverage;               /* Offset to Coverage table--from
65                                          * beginning of Substitution table */
66   SHORT         deltaGlyphID;           /* Add to original GlyphID to get
67                                          * substitute GlyphID */
68 };
69 ASSERT_SIZE (SingleSubstFormat1, 6);
70
71 struct SingleSubstFormat2
72 {
73   friend struct SingleSubst;
74
75   private:
76
77   inline bool apply (APPLY_ARG_DEF) const
78   {
79     hb_codepoint_t glyph_id = IN_CURGLYPH ();
80     unsigned int index = (this+coverage) (glyph_id);
81     if (HB_LIKELY (index == NOT_COVERED))
82       return false;
83
84     if (HB_UNLIKELY (index >= substitute.len))
85       return false;
86
87     glyph_id = substitute[index];
88     _hb_buffer_replace_glyph (buffer, glyph_id);
89
90     /* We inherit the old glyph class to the substituted glyph */
91     if (_hb_ot_layout_has_new_glyph_classes (context->face))
92       _hb_ot_layout_set_glyph_property (context->face, glyph_id, property);
93
94     return true;
95   }
96
97   inline bool sanitize (SANITIZE_ARG_DEF) {
98     SANITIZE_DEBUG ();
99     return SANITIZE_THIS (coverage) && SANITIZE (substitute);
100   }
101
102   private:
103   USHORT        format;                 /* Format identifier--format = 2 */
104   OffsetTo<Coverage>
105                 coverage;               /* Offset to Coverage table--from
106                                          * beginning of Substitution table */
107   ArrayOf<GlyphID>
108                 substitute;             /* Array of substitute
109                                          * GlyphIDs--ordered by Coverage Index */
110 };
111 ASSERT_SIZE (SingleSubstFormat2, 6);
112
113 struct SingleSubst
114 {
115   friend struct SubstLookupSubTable;
116
117   private:
118
119   inline bool apply (APPLY_ARG_DEF) const
120   {
121     switch (u.format) {
122     case 1: return u.format1->apply (APPLY_ARG);
123     case 2: return u.format2->apply (APPLY_ARG);
124     default:return false;
125     }
126   }
127
128   inline bool sanitize (SANITIZE_ARG_DEF) {
129     SANITIZE_DEBUG ();
130     if (!SANITIZE (u.format)) return false;
131     switch (u.format) {
132     case 1: return u.format1->sanitize (SANITIZE_ARG);
133     case 2: return u.format2->sanitize (SANITIZE_ARG);
134     default:return true;
135     }
136   }
137
138   private:
139   union {
140   USHORT                format;         /* Format identifier */
141   SingleSubstFormat1    format1[];
142   SingleSubstFormat2    format2[];
143   } u;
144 };
145 ASSERT_SIZE (SingleSubst, 2);
146
147
148 struct Sequence
149 {
150   friend struct MultipleSubstFormat1;
151
152   private:
153   inline bool apply (APPLY_ARG_DEF) const
154   {
155     if (HB_UNLIKELY (!substitute.len))
156       return false;
157
158     _hb_buffer_add_output_glyphs (buffer, 1,
159                                   substitute.len, (const uint16_t *) substitute.array,
160                                   0xFFFF, 0xFFFF);
161
162     /* This is a guess only ... */
163     if (_hb_ot_layout_has_new_glyph_classes (context->face))
164     {
165       if (property == HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE)
166         property = HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH;
167
168       unsigned int count = substitute.len;
169       for (unsigned int n = 0; n < count; n++)
170         _hb_ot_layout_set_glyph_property (context->face, substitute[n], property);
171     }
172
173     return true;
174   }
175
176   public:
177   inline bool sanitize (SANITIZE_ARG_DEF) {
178     SANITIZE_DEBUG ();
179     return SANITIZE (substitute);
180   }
181
182   private:
183   ArrayOf<GlyphID>
184                 substitute;             /* String of GlyphIDs to substitute */
185 };
186 ASSERT_SIZE (Sequence, 2);
187
188 struct MultipleSubstFormat1
189 {
190   friend struct MultipleSubst;
191
192   private:
193
194   inline bool apply (APPLY_ARG_DEF) const
195   {
196
197     unsigned int index = (this+coverage) (IN_CURGLYPH ());
198     if (HB_LIKELY (index == NOT_COVERED))
199       return false;
200
201     return (this+sequence[index]).apply (APPLY_ARG);
202   }
203
204   inline bool sanitize (SANITIZE_ARG_DEF) {
205     SANITIZE_DEBUG ();
206     return SANITIZE_THIS2 (coverage, sequence);
207   }
208
209   private:
210   USHORT        format;                 /* Format identifier--format = 1 */
211   OffsetTo<Coverage>
212                 coverage;               /* Offset to Coverage table--from
213                                          * beginning of Substitution table */
214   OffsetArrayOf<Sequence>
215                 sequence;               /* Array of Sequence tables
216                                          * ordered by Coverage Index */
217 };
218 ASSERT_SIZE (MultipleSubstFormat1, 6);
219
220 struct MultipleSubst
221 {
222   friend struct SubstLookupSubTable;
223
224   private:
225
226   inline bool apply (APPLY_ARG_DEF) const
227   {
228     switch (u.format) {
229     case 1: return u.format1->apply (APPLY_ARG);
230     default:return false;
231     }
232   }
233
234   inline bool sanitize (SANITIZE_ARG_DEF) {
235     SANITIZE_DEBUG ();
236     if (!SANITIZE (u.format)) return false;
237     switch (u.format) {
238     case 1: return u.format1->sanitize (SANITIZE_ARG);
239     default:return true;
240     }
241   }
242
243   private:
244   union {
245   USHORT                format;         /* Format identifier */
246   MultipleSubstFormat1  format1[];
247   } u;
248 };
249 ASSERT_SIZE (MultipleSubst, 2);
250
251
252 typedef ArrayOf<GlyphID> AlternateSet;  /* Array of alternate GlyphIDs--in
253                                          * arbitrary order */
254 ASSERT_SIZE (AlternateSet, 2);
255
256 struct AlternateSubstFormat1
257 {
258   friend struct AlternateSubst;
259
260   private:
261
262   inline bool apply (APPLY_ARG_DEF) const
263   {
264     hb_codepoint_t glyph_id = IN_CURGLYPH ();
265
266     unsigned int index = (this+coverage) (glyph_id);
267     if (HB_LIKELY (index == NOT_COVERED))
268       return false;
269
270     const AlternateSet &alt_set = this+alternateSet[index];
271
272     if (HB_UNLIKELY (!alt_set.len))
273       return false;
274
275     unsigned int alt_index = 0;
276
277     /* XXX callback to user to choose alternate
278     if (context->face->altfunc)
279       alt_index = (context->face->altfunc)(context->layout, buffer,
280                                     buffer->out_pos, glyph_id,
281                                     alt_set.len, alt_set.array);
282                                    */
283
284     if (HB_UNLIKELY (alt_index >= alt_set.len))
285       return false;
286
287     glyph_id = alt_set[alt_index];
288
289     _hb_buffer_replace_glyph (buffer, glyph_id);
290
291     /* We inherit the old glyph class to the substituted glyph */
292     if (_hb_ot_layout_has_new_glyph_classes (context->face))
293       _hb_ot_layout_set_glyph_property (context->face, glyph_id, property);
294
295     return true;
296   }
297
298   inline bool sanitize (SANITIZE_ARG_DEF) {
299     SANITIZE_DEBUG ();
300     return SANITIZE_THIS2 (coverage, alternateSet);
301   }
302
303   private:
304   USHORT        format;                 /* Format identifier--format = 1 */
305   OffsetTo<Coverage>
306                 coverage;               /* Offset to Coverage table--from
307                                          * beginning of Substitution table */
308   OffsetArrayOf<AlternateSet>
309                 alternateSet;           /* Array of AlternateSet tables
310                                          * ordered by Coverage Index */
311 };
312 ASSERT_SIZE (AlternateSubstFormat1, 6);
313
314 struct AlternateSubst
315 {
316   friend struct SubstLookupSubTable;
317
318   private:
319
320   inline bool apply (APPLY_ARG_DEF) const
321   {
322     switch (u.format) {
323     case 1: return u.format1->apply (APPLY_ARG);
324     default:return false;
325     }
326   }
327
328   inline bool sanitize (SANITIZE_ARG_DEF) {
329     SANITIZE_DEBUG ();
330     if (!SANITIZE (u.format)) return false;
331     switch (u.format) {
332     case 1: return u.format1->sanitize (SANITIZE_ARG);
333     default:return true;
334     }
335   }
336
337   private:
338   union {
339   USHORT                format;         /* Format identifier */
340   AlternateSubstFormat1 format1[];
341   } u;
342 };
343 ASSERT_SIZE (AlternateSubst, 2);
344
345
346 struct Ligature
347 {
348   friend struct LigatureSet;
349
350   private:
351   inline bool apply (APPLY_ARG_DEF, bool is_mark) const
352   {
353     unsigned int i, j;
354     unsigned int count = component.len;
355     unsigned int end = MIN (buffer->in_length, buffer->in_pos + context_length);
356     if (HB_UNLIKELY (buffer->in_pos + count > end))
357       return false;
358
359     for (i = 1, j = buffer->in_pos + 1; i < count; i++, j++)
360     {
361       while (_hb_ot_layout_skip_mark (context->face, IN_INFO (j), lookup_flag, &property))
362       {
363         if (HB_UNLIKELY (j + count - i == end))
364           return false;
365         j++;
366       }
367
368       if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK))
369         is_mark = FALSE;
370
371       if (HB_LIKELY (IN_GLYPH (j) != component[i]))
372         return false;
373     }
374     /* This is just a guess ... */
375     if (_hb_ot_layout_has_new_glyph_classes (context->face))
376       _hb_ot_layout_set_glyph_class (context->face, ligGlyph,
377                                      is_mark ? HB_OT_LAYOUT_GLYPH_CLASS_MARK
378                                              : HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE);
379
380     if (j == buffer->in_pos + i) /* No input glyphs skipped */
381       /* We don't use a new ligature ID if there are no skipped
382          glyphs and the ligature already has an ID. */
383       _hb_buffer_add_output_glyphs (buffer, i,
384                                     1, (const uint16_t *) &ligGlyph,
385                                     0xFFFF,
386                                     IN_LIGID (buffer->in_pos) ?
387                                     0xFFFF : _hb_buffer_allocate_lig_id (buffer));
388     else
389     {
390       unsigned int lig_id = _hb_buffer_allocate_lig_id (buffer);
391       _hb_buffer_add_output_glyph (buffer, ligGlyph, 0xFFFF, lig_id);
392
393       /* Now we must do a second loop to copy the skipped glyphs to
394          `out' and assign component values to it.  We start with the
395          glyph after the first component.  Glyphs between component
396          i and i+1 belong to component i.  Together with the lig_id
397          value it is later possible to check whether a specific
398          component value really belongs to a given ligature. */
399
400       for ( i = 1; i < count; i++ )
401       {
402         while (_hb_ot_layout_skip_mark (context->face, IN_CURINFO (), lookup_flag, NULL))
403           _hb_buffer_add_output_glyph (buffer, IN_CURGLYPH (), i - 1, lig_id);
404
405         (buffer->in_pos)++;
406       }
407
408       /* TODO We should possibly reassign lig_id and component for any
409        * components of a previous ligature that s now being removed as part of
410        * this ligature. */
411     }
412
413     return true;
414   }
415
416   public:
417   inline bool sanitize (SANITIZE_ARG_DEF) {
418     SANITIZE_DEBUG ();
419     return SANITIZE2 (ligGlyph, component);
420   }
421
422   private:
423   GlyphID       ligGlyph;               /* GlyphID of ligature to substitute */
424   HeadlessArrayOf<GlyphID>
425                 component;              /* Array of component GlyphIDs--start
426                                          * with the second  component--ordered
427                                          * in writing direction */
428 };
429 ASSERT_SIZE (Ligature, 4);
430
431 struct LigatureSet
432 {
433   friend struct LigatureSubstFormat1;
434
435   private:
436   inline bool apply (APPLY_ARG_DEF, bool is_mark) const
437   {
438     unsigned int num_ligs = ligature.len;
439     for (unsigned int i = 0; i < num_ligs; i++)
440     {
441       const Ligature &lig = this+ligature[i];
442       if (lig.apply (APPLY_ARG, is_mark))
443         return true;
444     }
445
446     return false;
447   }
448
449   public:
450   inline bool sanitize (SANITIZE_ARG_DEF) {
451     SANITIZE_DEBUG ();
452     return SANITIZE_THIS (ligature);
453   }
454
455   private:
456   OffsetArrayOf<Ligature>
457                 ligature;               /* Array LigatureSet tables
458                                          * ordered by preference */
459 };
460 ASSERT_SIZE (LigatureSet, 2);
461
462 struct LigatureSubstFormat1
463 {
464   friend struct LigatureSubst;
465
466   private:
467   inline bool apply (APPLY_ARG_DEF) const
468   {
469     hb_codepoint_t glyph_id = IN_CURGLYPH ();
470
471     bool first_is_mark = !!(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK);
472
473     unsigned int index = (this+coverage) (glyph_id);
474     if (HB_LIKELY (index == NOT_COVERED))
475       return false;
476
477     const LigatureSet &lig_set = this+ligatureSet[index];
478     return lig_set.apply (APPLY_ARG, first_is_mark);
479   }
480
481   inline bool sanitize (SANITIZE_ARG_DEF) {
482     SANITIZE_DEBUG ();
483     return SANITIZE_THIS2 (coverage, ligatureSet);
484   }
485
486   private:
487   USHORT        format;                 /* Format identifier--format = 1 */
488   OffsetTo<Coverage>
489                 coverage;               /* Offset to Coverage table--from
490                                          * beginning of Substitution table */
491   OffsetArrayOf<LigatureSet>
492                 ligatureSet;            /* Array LigatureSet tables
493                                          * ordered by Coverage Index */
494 };
495 ASSERT_SIZE (LigatureSubstFormat1, 6);
496
497 struct LigatureSubst
498 {
499   friend struct SubstLookupSubTable;
500
501   private:
502   inline bool apply (APPLY_ARG_DEF) const
503   {
504     switch (u.format) {
505     case 1: return u.format1->apply (APPLY_ARG);
506     default:return false;
507     }
508   }
509
510   inline bool sanitize (SANITIZE_ARG_DEF) {
511     SANITIZE_DEBUG ();
512     if (!SANITIZE (u.format)) return false;
513     switch (u.format) {
514     case 1: return u.format1->sanitize (SANITIZE_ARG);
515     default:return true;
516     }
517   }
518
519   private:
520   union {
521   USHORT                format;         /* Format identifier */
522   LigatureSubstFormat1  format1[];
523   } u;
524 };
525 ASSERT_SIZE (LigatureSubst, 2);
526
527
528
529 static inline bool substitute_lookup (APPLY_ARG_DEF, unsigned int lookup_index);
530
531 struct ContextSubst : Context
532 {
533   friend struct SubstLookupSubTable;
534
535   private:
536   inline bool apply (APPLY_ARG_DEF) const
537   { return Context::apply (APPLY_ARG, substitute_lookup); }
538 };
539 ASSERT_SIZE (ContextSubst, 2);
540
541 struct ChainContextSubst : ChainContext
542 {
543   friend struct SubstLookupSubTable;
544
545   private:
546   inline bool apply (APPLY_ARG_DEF) const
547   { return ChainContext::apply (APPLY_ARG, substitute_lookup); }
548 };
549 ASSERT_SIZE (ChainContextSubst, 2);
550
551
552 struct ExtensionSubst : Extension
553 {
554   friend struct SubstLookupSubTable;
555
556   private:
557   inline const struct SubstLookupSubTable& get_subtable (void) const
558   { return CONST_CAST (SubstLookupSubTable, Extension::get_subtable (), 0); }
559
560   inline bool apply (APPLY_ARG_DEF) const;
561
562   inline bool sanitize (SANITIZE_ARG_DEF);
563 };
564 ASSERT_SIZE (ExtensionSubst, 2);
565
566
567 struct ReverseChainSingleSubstFormat1
568 {
569   friend struct ReverseChainSingleSubst;
570
571   private:
572   inline bool apply (APPLY_ARG_DEF) const
573   {
574     if (HB_UNLIKELY (context_length != NO_CONTEXT))
575       return false; /* No chaining to this type */
576
577     unsigned int index = (this+coverage) (IN_CURGLYPH ());
578     if (HB_LIKELY (index == NOT_COVERED))
579       return false;
580
581     const OffsetArrayOf<Coverage> &lookahead = CONST_NEXT (OffsetArrayOf<Coverage>, backtrack);
582     const ArrayOf<GlyphID> &substitute = CONST_NEXT (ArrayOf<GlyphID>, lookahead);
583
584     if (match_backtrack (APPLY_ARG,
585                          backtrack.len, (USHORT *) backtrack.array,
586                          match_coverage, DECONST_CHARP(this)) &&
587         match_lookahead (APPLY_ARG,
588                          lookahead.len, (USHORT *) lookahead.array,
589                          match_coverage, DECONST_CHARP(this),
590                          1))
591     {
592       IN_CURGLYPH () = substitute[index];
593       buffer->in_pos--; /* Reverse! */
594       return true;
595     }
596
597     return false;
598   }
599
600   inline bool sanitize (SANITIZE_ARG_DEF) {
601     SANITIZE_DEBUG ();
602     if (!SANITIZE_THIS2 (coverage, backtrack))
603       return false;
604     OffsetArrayOf<Coverage> &lookahead = NEXT (OffsetArrayOf<Coverage>, backtrack);
605     if (!SANITIZE_THIS (lookahead))
606       return false;
607     ArrayOf<GlyphID> &substitute = NEXT (ArrayOf<GlyphID>, lookahead);
608     return SANITIZE (substitute);
609   }
610
611   private:
612   USHORT        format;                 /* Format identifier--format = 1 */
613   OffsetTo<Coverage>
614                 coverage;               /* Offset to Coverage table--from
615                                          * beginning of table */
616   OffsetArrayOf<Coverage>
617                 backtrack;              /* Array of coverage tables
618                                          * in backtracking sequence, in  glyph
619                                          * sequence order */
620   OffsetArrayOf<Coverage>
621                 lookaheadX;             /* Array of coverage tables
622                                          * in lookahead sequence, in glyph
623                                          * sequence order */
624   ArrayOf<GlyphID>
625                 substituteX;            /* Array of substitute
626                                          * GlyphIDs--ordered by Coverage Index */
627 };
628 ASSERT_SIZE (ReverseChainSingleSubstFormat1, 10);
629
630 struct ReverseChainSingleSubst
631 {
632   friend struct SubstLookupSubTable;
633
634   private:
635   inline bool apply (APPLY_ARG_DEF) const
636   {
637     switch (u.format) {
638     case 1: return u.format1->apply (APPLY_ARG);
639     default:return false;
640     }
641   }
642
643   inline bool sanitize (SANITIZE_ARG_DEF) {
644     SANITIZE_DEBUG ();
645     if (!SANITIZE (u.format)) return false;
646     switch (u.format) {
647     case 1: return u.format1->sanitize (SANITIZE_ARG);
648     default:return true;
649     }
650   }
651
652   private:
653   union {
654   USHORT                                format;         /* Format identifier */
655   ReverseChainSingleSubstFormat1        format1[];
656   } u;
657 };
658 ASSERT_SIZE (ReverseChainSingleSubst, 2);
659
660
661
662 /*
663  * SubstLookup
664  */
665
666 struct SubstLookupSubTable
667 {
668   friend struct SubstLookup;
669
670   enum {
671     Single              = 1,
672     Multiple            = 2,
673     Alternate           = 3,
674     Ligature            = 4,
675     Context             = 5,
676     ChainContext        = 6,
677     Extension           = 7,
678     ReverseChainSingle  = 8,
679   };
680
681   bool apply (APPLY_ARG_DEF, unsigned int lookup_type) const
682   {
683     switch (lookup_type) {
684     case Single:                return u.single->apply (APPLY_ARG);
685     case Multiple:              return u.multiple->apply (APPLY_ARG);
686     case Alternate:             return u.alternate->apply (APPLY_ARG);
687     case Ligature:              return u.ligature->apply (APPLY_ARG);
688     case Context:               return u.context->apply (APPLY_ARG);
689     case ChainContext:          return u.chainContext->apply (APPLY_ARG);
690     case Extension:             return u.extension->apply (APPLY_ARG);
691     case ReverseChainSingle:    return u.reverseChainContextSingle->apply (APPLY_ARG);
692     default:return false;
693     }
694   }
695
696   bool sanitize (SANITIZE_ARG_DEF) {
697     SANITIZE_DEBUG ();
698     if (!SANITIZE (u.format)) return false;
699     switch (u.format) {
700     case Single:                return u.single->sanitize (SANITIZE_ARG);
701     case Multiple:              return u.multiple->sanitize (SANITIZE_ARG);
702     case Alternate:             return u.alternate->sanitize (SANITIZE_ARG);
703     case Ligature:              return u.ligature->sanitize (SANITIZE_ARG);
704     case Context:               return u.context->sanitize (SANITIZE_ARG);
705     case ChainContext:          return u.chainContext->sanitize (SANITIZE_ARG);
706     case Extension:             return u.extension->sanitize (SANITIZE_ARG);
707     case ReverseChainSingle:    return u.reverseChainContextSingle->sanitize (SANITIZE_ARG);
708     default:return true;
709     }
710   }
711
712   private:
713   union {
714   USHORT                        format;
715   SingleSubst                   single[];
716   MultipleSubst                 multiple[];
717   AlternateSubst                alternate[];
718   LigatureSubst                 ligature[];
719   ContextSubst                  context[];
720   ChainContextSubst             chainContext[];
721   ExtensionSubst                extension[];
722   ReverseChainSingleSubst       reverseChainContextSingle[];
723   } u;
724 };
725 ASSERT_SIZE (SubstLookupSubTable, 2);
726
727
728 struct SubstLookup : Lookup
729 {
730   inline const SubstLookupSubTable& get_subtable (unsigned int i) const
731   { return CONST_CAST (SubstLookupSubTable, Lookup::get_subtable (i), 0); }
732
733   /* Like get_type(), but looks through extension lookups.
734    * Never returns Extension */
735   inline unsigned int get_effective_type (void) const
736   {
737     unsigned int type = get_type ();
738
739     if (HB_UNLIKELY (type == SubstLookupSubTable::Extension))
740     {
741       unsigned int count = get_subtable_count ();
742       type = get_subtable(0).u.extension->get_type ();
743       /* The spec says all subtables should have the same type.
744        * This is specially important if one has a reverse type! */
745       for (unsigned int i = 1; i < count; i++)
746         if (get_subtable(i).u.extension->get_type () != type)
747           return 0;
748     }
749
750     return type;
751   }
752
753   inline bool is_reverse (void) const
754   { return HB_UNLIKELY (get_effective_type () == SubstLookupSubTable::ReverseChainSingle); }
755
756   inline bool apply_once (hb_ot_layout_context_t *context,
757                           hb_buffer_t    *buffer,
758                           unsigned int    context_length,
759                           unsigned int    nesting_level_left) const
760   {
761     unsigned int lookup_type = get_type ();
762     unsigned int lookup_flag = get_flag ();
763     unsigned int property;
764
765     if (!_hb_ot_layout_check_glyph_property (context->face, IN_CURINFO (), lookup_flag, &property))
766       return false;
767
768     unsigned int count = get_subtable_count ();
769     for (unsigned int i = 0; i < count; i++)
770       if (get_subtable (i).apply (APPLY_ARG, lookup_type))
771         return true;
772
773     return false;
774   }
775
776   bool apply_string (hb_ot_layout_context_t *context,
777                      hb_buffer_t *buffer,
778                      hb_mask_t    mask) const
779   {
780     bool ret = false;
781
782     if (HB_UNLIKELY (!buffer->in_length))
783       return false;
784
785     if (HB_LIKELY (!is_reverse ()))
786     {
787         /* in/out forward substitution */
788         _hb_buffer_clear_output (buffer);
789         buffer->in_pos = 0;
790         while (buffer->in_pos < buffer->in_length)
791         {
792           if ((~IN_MASK (buffer->in_pos) & mask) &&
793               apply_once (context, buffer, NO_CONTEXT, MAX_NESTING_LEVEL))
794             ret = true;
795           else
796             _hb_buffer_next_glyph (buffer);
797
798         }
799         if (ret)
800           _hb_buffer_swap (buffer);
801
802     }
803     else
804     {
805
806         /* in-place backward substitution */
807         buffer->in_pos = buffer->in_length - 1;
808         do
809         {
810           if ((~IN_MASK (buffer->in_pos) & mask) &&
811               apply_once (context, buffer, NO_CONTEXT, MAX_NESTING_LEVEL))
812             ret = true;
813           else
814             buffer->in_pos--;
815
816         }
817         while ((int) buffer->in_pos >= 0);
818     }
819
820     return ret;
821   }
822
823   inline bool sanitize (SANITIZE_ARG_DEF) {
824     SANITIZE_DEBUG ();
825     if (!Lookup::sanitize (SANITIZE_ARG)) return false;
826     OffsetArrayOf<SubstLookupSubTable> &list = (OffsetArrayOf<SubstLookupSubTable> &) subTable;
827     return SANITIZE_THIS (list);
828   }
829 };
830 ASSERT_SIZE (SubstLookup, 6);
831
832 typedef OffsetListOf<SubstLookup> SubstLookupList;
833 ASSERT_SIZE (SubstLookupList, 2);
834
835 /*
836  * GSUB
837  */
838
839 struct GSUB : GSUBGPOS
840 {
841   static const hb_tag_t Tag     = HB_OT_TAG_GSUB;
842
843   static inline const GSUB& get_for_data (const char *data)
844   { return (const GSUB&) GSUBGPOS::get_for_data (data); }
845
846   inline const SubstLookup& get_lookup (unsigned int i) const
847   { return (const SubstLookup&) GSUBGPOS::get_lookup (i); }
848
849   inline bool substitute_lookup (hb_ot_layout_context_t *context,
850                                  hb_buffer_t  *buffer,
851                                  unsigned int  lookup_index,
852                                  hb_mask_t     mask) const
853   { return get_lookup (lookup_index).apply_string (context, buffer, mask); }
854
855
856   bool sanitize (SANITIZE_ARG_DEF) {
857     SANITIZE_DEBUG ();
858     if (!GSUBGPOS::sanitize (SANITIZE_ARG)) return false;
859     OffsetTo<SubstLookupList> &list = CAST(OffsetTo<SubstLookupList>, lookupList, 0);
860     return SANITIZE_THIS (list);
861   }
862 };
863 ASSERT_SIZE (GSUB, 10);
864
865
866 /* Out-of-class implementation for methods recursing */
867
868 inline bool ExtensionSubst::apply (APPLY_ARG_DEF) const
869 {
870   unsigned int lookup_type = get_type ();
871
872   if (HB_UNLIKELY (lookup_type == SubstLookupSubTable::Extension))
873     return false;
874
875   return get_subtable ().apply (APPLY_ARG, lookup_type);
876 }
877
878 inline bool ExtensionSubst::sanitize (SANITIZE_ARG_DEF)
879 {
880   SANITIZE_DEBUG ();
881   return Extension::sanitize (SANITIZE_ARG) &&
882          (&(Extension::get_subtable ()) == &Null(LookupSubTable) ||
883           get_type () == SubstLookupSubTable::Extension ||
884           DECONST_CAST (SubstLookupSubTable, get_subtable (), 0).sanitize (SANITIZE_ARG));
885 }
886
887 static inline bool substitute_lookup (APPLY_ARG_DEF, unsigned int lookup_index)
888 {
889   const GSUB &gsub = *(context->face->ot_layout.gsub);
890   const SubstLookup &l = gsub.get_lookup (lookup_index);
891
892   if (HB_UNLIKELY (nesting_level_left == 0))
893     return false;
894   nesting_level_left--;
895
896   if (HB_UNLIKELY (context_length < 1))
897     return false;
898
899   return l.apply_once (context, buffer, context_length, nesting_level_left);
900 }
901
902
903 #endif /* HB_OT_LAYOUT_GSUB_PRIVATE_HH */