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