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