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