Start ft glue
[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     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 (buffer, 1,
162                                   substitute.len, (const uint16_t *) substitute.const_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 (buffer, i,
390                                     1, (const uint16_t *) &ligGlyph,
391                                     0xFFFF,
392                                     IN_LIGID (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 - 1, lig_id);
410
411         (buffer->in_pos)++;
412       }
413
414       /* TODO We should possibly reassign lig_id and component for any
415        * components of a previous ligature that s now being removed as part of
416        * this ligature. */
417     }
418
419     return true;
420   }
421
422   public:
423   inline bool sanitize (SANITIZE_ARG_DEF) {
424     TRACE_SANITIZE ();
425     return SANITIZE2 (ligGlyph, component);
426   }
427
428   private:
429   GlyphID       ligGlyph;               /* GlyphID of ligature to substitute */
430   HeadlessArrayOf<GlyphID>
431                 component;              /* Array of component GlyphIDs--start
432                                          * with the second  component--ordered
433                                          * in writing direction */
434 };
435 ASSERT_SIZE (Ligature, 4);
436
437 struct LigatureSet
438 {
439   friend struct LigatureSubstFormat1;
440
441   private:
442   inline bool apply (APPLY_ARG_DEF, bool is_mark) const
443   {
444     TRACE_APPLY ();
445     unsigned int num_ligs = ligature.len;
446     for (unsigned int i = 0; i < num_ligs; i++)
447     {
448       const Ligature &lig = this+ligature[i];
449       if (lig.apply (APPLY_ARG, is_mark))
450         return true;
451     }
452
453     return false;
454   }
455
456   public:
457   inline bool sanitize (SANITIZE_ARG_DEF) {
458     TRACE_SANITIZE ();
459     return SANITIZE_THIS (ligature);
460   }
461
462   private:
463   OffsetArrayOf<Ligature>
464                 ligature;               /* Array LigatureSet tables
465                                          * ordered by preference */
466 };
467 ASSERT_SIZE (LigatureSet, 2);
468
469 struct LigatureSubstFormat1
470 {
471   friend struct LigatureSubst;
472
473   private:
474   inline bool apply (APPLY_ARG_DEF) const
475   {
476     TRACE_APPLY ();
477     hb_codepoint_t glyph_id = IN_CURGLYPH ();
478
479     bool first_is_mark = !!(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK);
480
481     unsigned int index = (this+coverage) (glyph_id);
482     if (HB_LIKELY (index == NOT_COVERED))
483       return false;
484
485     const LigatureSet &lig_set = this+ligatureSet[index];
486     return lig_set.apply (APPLY_ARG, first_is_mark);
487   }
488
489   inline bool sanitize (SANITIZE_ARG_DEF) {
490     TRACE_SANITIZE ();
491     return SANITIZE_THIS2 (coverage, ligatureSet);
492   }
493
494   private:
495   USHORT        format;                 /* Format identifier--format = 1 */
496   OffsetTo<Coverage>
497                 coverage;               /* Offset to Coverage table--from
498                                          * beginning of Substitution table */
499   OffsetArrayOf<LigatureSet>
500                 ligatureSet;            /* Array LigatureSet tables
501                                          * ordered by Coverage Index */
502 };
503 ASSERT_SIZE (LigatureSubstFormat1, 6);
504
505 struct LigatureSubst
506 {
507   friend struct SubstLookupSubTable;
508
509   private:
510   inline bool apply (APPLY_ARG_DEF) const
511   {
512     TRACE_APPLY ();
513     switch (u.format) {
514     case 1: return u.format1->apply (APPLY_ARG);
515     default:return false;
516     }
517   }
518
519   inline bool sanitize (SANITIZE_ARG_DEF) {
520     TRACE_SANITIZE ();
521     if (!SANITIZE (u.format)) return false;
522     switch (u.format) {
523     case 1: return u.format1->sanitize (SANITIZE_ARG);
524     default:return true;
525     }
526   }
527
528   private:
529   union {
530   USHORT                format;         /* Format identifier */
531   LigatureSubstFormat1  format1[VAR];
532   } u;
533 };
534
535
536
537 static inline bool substitute_lookup (APPLY_ARG_DEF, unsigned int lookup_index);
538
539 struct ContextSubst : Context
540 {
541   friend struct SubstLookupSubTable;
542
543   private:
544   inline bool apply (APPLY_ARG_DEF) const
545   {
546     TRACE_APPLY ();
547     return Context::apply (APPLY_ARG, substitute_lookup);
548   }
549 };
550
551 struct ChainContextSubst : ChainContext
552 {
553   friend struct SubstLookupSubTable;
554
555   private:
556   inline bool apply (APPLY_ARG_DEF) const
557   {
558     TRACE_APPLY ();
559     return ChainContext::apply (APPLY_ARG, substitute_lookup);
560   }
561 };
562
563
564 struct ExtensionSubst : Extension
565 {
566   friend struct SubstLookupSubTable;
567
568   private:
569   inline const struct SubstLookupSubTable& get_subtable (void) const
570   { return CONST_CAST (SubstLookupSubTable, Extension::get_subtable (), 0); }
571
572   inline bool apply (APPLY_ARG_DEF) const;
573
574   inline bool sanitize (SANITIZE_ARG_DEF);
575 };
576
577
578 struct ReverseChainSingleSubstFormat1
579 {
580   friend struct ReverseChainSingleSubst;
581
582   private:
583   inline bool apply (APPLY_ARG_DEF) const
584   {
585     TRACE_APPLY ();
586     if (HB_UNLIKELY (context_length != NO_CONTEXT))
587       return false; /* No chaining to this type */
588
589     unsigned int index = (this+coverage) (IN_CURGLYPH ());
590     if (HB_LIKELY (index == NOT_COVERED))
591       return false;
592
593     const OffsetArrayOf<Coverage> &lookahead = CONST_NEXT (OffsetArrayOf<Coverage>, backtrack);
594     const ArrayOf<GlyphID> &substitute = CONST_NEXT (ArrayOf<GlyphID>, lookahead);
595
596     if (match_backtrack (APPLY_ARG,
597                          backtrack.len, (USHORT *) backtrack.const_array(),
598                          match_coverage, DECONST_CHARP(this)) &&
599         match_lookahead (APPLY_ARG,
600                          lookahead.len, (USHORT *) lookahead.const_array(),
601                          match_coverage, DECONST_CHARP(this),
602                          1))
603     {
604       IN_CURGLYPH () = substitute[index];
605       buffer->in_pos--; /* Reverse! */
606       return true;
607     }
608
609     return false;
610   }
611
612   inline bool sanitize (SANITIZE_ARG_DEF) {
613     TRACE_SANITIZE ();
614     if (!SANITIZE_THIS2 (coverage, backtrack))
615       return false;
616     OffsetArrayOf<Coverage> &lookahead = NEXT (OffsetArrayOf<Coverage>, backtrack);
617     if (!SANITIZE_THIS (lookahead))
618       return false;
619     ArrayOf<GlyphID> &substitute = NEXT (ArrayOf<GlyphID>, lookahead);
620     return SANITIZE (substitute);
621   }
622
623   private:
624   USHORT        format;                 /* Format identifier--format = 1 */
625   OffsetTo<Coverage>
626                 coverage;               /* Offset to Coverage table--from
627                                          * beginning of table */
628   OffsetArrayOf<Coverage>
629                 backtrack;              /* Array of coverage tables
630                                          * in backtracking sequence, in  glyph
631                                          * sequence order */
632   OffsetArrayOf<Coverage>
633                 lookaheadX;             /* Array of coverage tables
634                                          * in lookahead sequence, in glyph
635                                          * sequence order */
636   ArrayOf<GlyphID>
637                 substituteX;            /* Array of substitute
638                                          * GlyphIDs--ordered by Coverage Index */
639 };
640 ASSERT_SIZE (ReverseChainSingleSubstFormat1, 10);
641
642 struct ReverseChainSingleSubst
643 {
644   friend struct SubstLookupSubTable;
645
646   private:
647   inline bool apply (APPLY_ARG_DEF) const
648   {
649     TRACE_APPLY ();
650     switch (u.format) {
651     case 1: return u.format1->apply (APPLY_ARG);
652     default:return false;
653     }
654   }
655
656   inline bool sanitize (SANITIZE_ARG_DEF) {
657     TRACE_SANITIZE ();
658     if (!SANITIZE (u.format)) return false;
659     switch (u.format) {
660     case 1: return u.format1->sanitize (SANITIZE_ARG);
661     default:return true;
662     }
663   }
664
665   private:
666   union {
667   USHORT                                format;         /* Format identifier */
668   ReverseChainSingleSubstFormat1        format1[VAR];
669   } u;
670 };
671
672
673
674 /*
675  * SubstLookup
676  */
677
678 struct SubstLookupSubTable
679 {
680   friend struct SubstLookup;
681
682   enum {
683     Single              = 1,
684     Multiple            = 2,
685     Alternate           = 3,
686     Ligature            = 4,
687     Context             = 5,
688     ChainContext        = 6,
689     Extension           = 7,
690     ReverseChainSingle  = 8
691   };
692
693   inline bool apply (APPLY_ARG_DEF, unsigned int lookup_type) const
694   {
695     TRACE_APPLY ();
696     switch (lookup_type) {
697     case Single:                return u.single->apply (APPLY_ARG);
698     case Multiple:              return u.multiple->apply (APPLY_ARG);
699     case Alternate:             return u.alternate->apply (APPLY_ARG);
700     case Ligature:              return u.ligature->apply (APPLY_ARG);
701     case Context:               return u.context->apply (APPLY_ARG);
702     case ChainContext:          return u.chainContext->apply (APPLY_ARG);
703     case Extension:             return u.extension->apply (APPLY_ARG);
704     case ReverseChainSingle:    return u.reverseChainContextSingle->apply (APPLY_ARG);
705     default:return false;
706     }
707   }
708
709   inline bool sanitize (SANITIZE_ARG_DEF) {
710     TRACE_SANITIZE ();
711     if (!SANITIZE (u.format)) return false;
712     switch (u.format) {
713     case Single:                return u.single->sanitize (SANITIZE_ARG);
714     case Multiple:              return u.multiple->sanitize (SANITIZE_ARG);
715     case Alternate:             return u.alternate->sanitize (SANITIZE_ARG);
716     case Ligature:              return u.ligature->sanitize (SANITIZE_ARG);
717     case Context:               return u.context->sanitize (SANITIZE_ARG);
718     case ChainContext:          return u.chainContext->sanitize (SANITIZE_ARG);
719     case Extension:             return u.extension->sanitize (SANITIZE_ARG);
720     case ReverseChainSingle:    return u.reverseChainContextSingle->sanitize (SANITIZE_ARG);
721     default:return true;
722     }
723   }
724
725   private:
726   union {
727   USHORT                        format;
728   SingleSubst                   single[VAR];
729   MultipleSubst                 multiple[VAR];
730   AlternateSubst                alternate[VAR];
731   LigatureSubst                 ligature[VAR];
732   ContextSubst                  context[VAR];
733   ChainContextSubst             chainContext[VAR];
734   ExtensionSubst                extension[VAR];
735   ReverseChainSingleSubst       reverseChainContextSingle[VAR];
736   } u;
737 };
738
739
740 struct SubstLookup : Lookup
741 {
742   inline const SubstLookupSubTable& get_subtable (unsigned int i) const
743   { return CONST_CAST (SubstLookupSubTable, Lookup::get_subtable (i), 0); }
744
745   /* Like get_type(), but looks through extension lookups.
746    * Never returns Extension */
747   inline unsigned int get_effective_type (void) const
748   {
749     unsigned int type = get_type ();
750
751     if (HB_UNLIKELY (type == SubstLookupSubTable::Extension))
752     {
753       unsigned int count = get_subtable_count ();
754       type = get_subtable(0).u.extension->get_type ();
755       /* The spec says all subtables should have the same type.
756        * This is specially important if one has a reverse type! */
757       for (unsigned int i = 1; i < count; i++)
758         if (get_subtable(i).u.extension->get_type () != type)
759           return 0;
760     }
761
762     return type;
763   }
764
765   inline bool is_reverse (void) const
766   { return HB_UNLIKELY (get_effective_type () == SubstLookupSubTable::ReverseChainSingle); }
767
768   inline bool apply_once (hb_ot_layout_context_t *context,
769                           hb_buffer_t    *buffer,
770                           unsigned int    context_length,
771                           unsigned int    nesting_level_left) const
772   {
773     unsigned int lookup_type = get_type ();
774     unsigned int lookup_flag = get_flag ();
775     unsigned int property;
776
777     if (!_hb_ot_layout_check_glyph_property (context->face, IN_CURINFO (), lookup_flag, &property))
778       return false;
779
780     unsigned int count = get_subtable_count ();
781     for (unsigned int i = 0; i < count; i++)
782       if (get_subtable (i).apply (APPLY_ARG_INIT, lookup_type))
783         return true;
784
785     return false;
786   }
787
788   inline bool apply_string (hb_ot_layout_context_t *context,
789                             hb_buffer_t *buffer,
790                             hb_mask_t    mask) const
791   {
792     bool ret = false;
793
794     if (HB_UNLIKELY (!buffer->in_length))
795       return false;
796
797     if (HB_LIKELY (!is_reverse ()))
798     {
799         /* in/out forward substitution */
800         _hb_buffer_clear_output (buffer);
801         buffer->in_pos = 0;
802         while (buffer->in_pos < buffer->in_length)
803         {
804           if ((~IN_MASK (buffer->in_pos) & mask) &&
805               apply_once (context, buffer, NO_CONTEXT, MAX_NESTING_LEVEL))
806             ret = true;
807           else
808             _hb_buffer_next_glyph (buffer);
809
810         }
811         if (ret)
812           _hb_buffer_swap (buffer);
813     }
814     else
815     {
816         /* in-place backward substitution */
817         buffer->in_pos = buffer->in_length - 1;
818         do
819         {
820           if ((~IN_MASK (buffer->in_pos) & mask) &&
821               apply_once (context, buffer, NO_CONTEXT, MAX_NESTING_LEVEL))
822             ret = true;
823           else
824             buffer->in_pos--;
825
826         }
827         while ((int) buffer->in_pos >= 0);
828     }
829
830     return ret;
831   }
832
833   inline bool sanitize (SANITIZE_ARG_DEF) {
834     TRACE_SANITIZE ();
835     if (!Lookup::sanitize (SANITIZE_ARG)) return false;
836     OffsetArrayOf<SubstLookupSubTable> &list = (OffsetArrayOf<SubstLookupSubTable> &) subTable;
837     return SANITIZE_THIS (list);
838   }
839 };
840
841 typedef OffsetListOf<SubstLookup> SubstLookupList;
842 ASSERT_SIZE (SubstLookupList, 2);
843
844 /*
845  * GSUB
846  */
847
848 struct GSUB : GSUBGPOS
849 {
850   static const hb_tag_t Tag     = HB_OT_TAG_GSUB;
851
852   static inline const GSUB& get_for_data (const char *data)
853   { return (const GSUB&) GSUBGPOS::get_for_data (data); }
854
855   inline const SubstLookup& get_lookup (unsigned int i) const
856   { return (const SubstLookup&) GSUBGPOS::get_lookup (i); }
857
858   inline bool substitute_lookup (hb_ot_layout_context_t *context,
859                                  hb_buffer_t  *buffer,
860                                  unsigned int  lookup_index,
861                                  hb_mask_t     mask) const
862   { return get_lookup (lookup_index).apply_string (context, buffer, mask); }
863
864
865   inline bool sanitize (SANITIZE_ARG_DEF) {
866     TRACE_SANITIZE ();
867     if (!GSUBGPOS::sanitize (SANITIZE_ARG)) return false;
868     OffsetTo<SubstLookupList> &list = CAST(OffsetTo<SubstLookupList>, lookupList, 0);
869     return SANITIZE_THIS (list);
870   }
871 };
872 ASSERT_SIZE (GSUB, 10);
873
874
875 /* Out-of-class implementation for methods recursing */
876
877 inline bool ExtensionSubst::apply (APPLY_ARG_DEF) const
878 {
879   TRACE_APPLY ();
880   unsigned int lookup_type = get_type ();
881
882   if (HB_UNLIKELY (lookup_type == SubstLookupSubTable::Extension))
883     return false;
884
885   return get_subtable ().apply (APPLY_ARG, lookup_type);
886 }
887
888 inline bool ExtensionSubst::sanitize (SANITIZE_ARG_DEF)
889 {
890   TRACE_SANITIZE ();
891   return Extension::sanitize (SANITIZE_ARG) &&
892          (&(Extension::get_subtable ()) == &Null(LookupSubTable) ||
893           get_type () == SubstLookupSubTable::Extension ||
894           DECONST_CAST (SubstLookupSubTable, get_subtable (), 0).sanitize (SANITIZE_ARG));
895 }
896
897 static inline bool substitute_lookup (APPLY_ARG_DEF, unsigned int lookup_index)
898 {
899   const GSUB &gsub = *(context->face->ot_layout.gsub);
900   const SubstLookup &l = gsub.get_lookup (lookup_index);
901
902   if (HB_UNLIKELY (nesting_level_left == 0))
903     return false;
904   nesting_level_left--;
905
906   if (HB_UNLIKELY (context_length < 1))
907     return false;
908
909   return l.apply_once (context, buffer, context_length, nesting_level_left);
910 }
911
912
913 #endif /* HB_OT_LAYOUT_GSUB_PRIVATE_HH */