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