422ee43379545db0880270846e823df3e1767222
[apps/home/video-player.git] / src / hb-ot-layout-gsubgpos-private.hh
1 /*
2  * Copyright © 2007,2008,2009,2010  Red Hat, Inc.
3  * Copyright © 2010  Google, Inc.
4  *
5  *  This is part of HarfBuzz, a text shaping library.
6  *
7  * Permission is hereby granted, without written agreement and without
8  * license or royalty fees, to use, copy, modify, and distribute this
9  * software and its documentation for any purpose, provided that the
10  * above copyright notice and the following two paragraphs appear in
11  * all copies of this software.
12  *
13  * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
14  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
15  * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
16  * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
17  * DAMAGE.
18  *
19  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
20  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
21  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
22  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
23  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
24  *
25  * Red Hat Author(s): Behdad Esfahbod
26  * Google Author(s): Behdad Esfahbod
27  */
28
29 #ifndef HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH
30 #define HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH
31
32 #include "hb-buffer-private.hh"
33 #include "hb-ot-layout-gdef-private.hh"
34
35 HB_BEGIN_DECLS
36
37
38 /* buffer var allocations */
39 #define lig_id() var2.u16[0] /* unique ligature id */
40 #define lig_comp() var2.u16[1] /* component number in the ligature (0 = base) */
41
42
43 #ifndef HB_DEBUG_APPLY
44 #define HB_DEBUG_APPLY (HB_DEBUG+0)
45 #endif
46
47 #define TRACE_APPLY() \
48         hb_trace_t<HB_DEBUG_APPLY> trace (&c->debug_depth, "APPLY", HB_FUNC, this); \
49
50
51 HB_BEGIN_DECLS
52
53 struct hb_apply_context_t
54 {
55   unsigned int debug_depth;
56   hb_font_t *font;
57   hb_face_t *face;
58   hb_buffer_t *buffer;
59   hb_mask_t lookup_mask;
60   unsigned int context_length;
61   unsigned int nesting_level_left;
62   unsigned int lookup_props;
63   unsigned int property; /* propety of first glyph */
64
65
66   inline void replace_glyph (hb_codepoint_t glyph_index) const
67   {
68     clear_property ();
69     buffer->replace_glyph (glyph_index);
70   }
71   inline void replace_glyphs_be16 (unsigned int num_in,
72                                    unsigned int num_out,
73                                    const uint16_t *glyph_data_be) const
74   {
75     clear_property ();
76     buffer->replace_glyphs_be16 (num_in, num_out, glyph_data_be);
77   }
78
79   inline void guess_glyph_class (unsigned int klass)
80   {
81     /* XXX if ! has gdef */
82     buffer->info[buffer->i].props_cache() = klass;
83   }
84
85   private:
86   inline void clear_property (void) const
87   {
88     /* XXX if has gdef */
89     buffer->info[buffer->i].props_cache() = 0;
90   }
91 };
92
93
94
95 typedef bool (*match_func_t) (hb_codepoint_t glyph_id, const USHORT &value, const void *data);
96 typedef bool (*apply_lookup_func_t) (hb_apply_context_t *c, unsigned int lookup_index);
97
98 struct ContextFuncs
99 {
100   match_func_t match;
101   apply_lookup_func_t apply;
102 };
103
104
105 static inline bool match_glyph (hb_codepoint_t glyph_id, const USHORT &value, const void *data HB_UNUSED)
106 {
107   return glyph_id == value;
108 }
109
110 static inline bool match_class (hb_codepoint_t glyph_id, const USHORT &value, const void *data)
111 {
112   const ClassDef &class_def = *reinterpret_cast<const ClassDef *>(data);
113   return class_def.get_class (glyph_id) == value;
114 }
115
116 static inline bool match_coverage (hb_codepoint_t glyph_id, const USHORT &value, const void *data)
117 {
118   const OffsetTo<Coverage> &coverage = (const OffsetTo<Coverage>&)value;
119   return (data+coverage) (glyph_id) != NOT_COVERED;
120 }
121
122
123 static inline bool match_input (hb_apply_context_t *c,
124                                 unsigned int count, /* Including the first glyph (not matched) */
125                                 const USHORT input[], /* Array of input values--start with second glyph */
126                                 match_func_t match_func,
127                                 const void *match_data,
128                                 unsigned int *context_length_out)
129 {
130   unsigned int i, j;
131   unsigned int end = MIN (c->buffer->len, c->buffer->i + c->context_length);
132   if (unlikely (c->buffer->i + count > end))
133     return false;
134
135   for (i = 1, j = c->buffer->i + 1; i < count; i++, j++)
136   {
137     while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[j], c->lookup_props, NULL))
138     {
139       if (unlikely (j + count - i == end))
140         return false;
141       j++;
142     }
143
144     if (likely (!match_func (c->buffer->info[j].codepoint, input[i - 1], match_data)))
145       return false;
146   }
147
148   *context_length_out = j - c->buffer->i;
149
150   return true;
151 }
152
153 static inline bool match_backtrack (hb_apply_context_t *c,
154                                     unsigned int count,
155                                     const USHORT backtrack[],
156                                     match_func_t match_func,
157                                     const void *match_data)
158 {
159   if (unlikely (c->buffer->backtrack_len () < count))
160     return false;
161
162   for (unsigned int i = 0, j = c->buffer->backtrack_len () - 1; i < count; i++, j--)
163   {
164     while (_hb_ot_layout_skip_mark (c->face, &c->buffer->out_info[j], c->lookup_props, NULL))
165     {
166       if (unlikely (j + 1 == count - i))
167         return false;
168       j--;
169     }
170
171     if (likely (!match_func (c->buffer->out_info[j].codepoint, backtrack[i], match_data)))
172       return false;
173   }
174
175   return true;
176 }
177
178 static inline bool match_lookahead (hb_apply_context_t *c,
179                                     unsigned int count,
180                                     const USHORT lookahead[],
181                                     match_func_t match_func,
182                                     const void *match_data,
183                                     unsigned int offset)
184 {
185   unsigned int i, j;
186   unsigned int end = MIN (c->buffer->len, c->buffer->i + c->context_length);
187   if (unlikely (c->buffer->i + offset + count > end))
188     return false;
189
190   for (i = 0, j = c->buffer->i + offset; i < count; i++, j++)
191   {
192     while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[j], c->lookup_props, NULL))
193     {
194       if (unlikely (j + count - i == end))
195         return false;
196       j++;
197     }
198
199     if (likely (!match_func (c->buffer->info[j].codepoint, lookahead[i], match_data)))
200       return false;
201   }
202
203   return true;
204 }
205
206 HB_END_DECLS
207
208
209 struct LookupRecord
210 {
211   inline bool sanitize (hb_sanitize_context_t *c) {
212     TRACE_SANITIZE ();
213     return c->check_struct (this);
214   }
215
216   USHORT        sequenceIndex;          /* Index into current glyph
217                                          * sequence--first glyph = 0 */
218   USHORT        lookupListIndex;        /* Lookup to apply to that
219                                          * position--zero--based */
220   public:
221   DEFINE_SIZE_STATIC (4);
222 };
223
224
225 HB_BEGIN_DECLS
226
227 static inline bool apply_lookup (hb_apply_context_t *c,
228                                  unsigned int count, /* Including the first glyph */
229                                  unsigned int lookupCount,
230                                  const LookupRecord lookupRecord[], /* Array of LookupRecords--in design order */
231                                  apply_lookup_func_t apply_func)
232 {
233   unsigned int end = MIN (c->buffer->len, c->buffer->i + c->context_length);
234   if (unlikely (count == 0 || c->buffer->i + count > end))
235     return false;
236
237   /* TODO We don't support lookupRecord arrays that are not increasing:
238    *      Should be easy for in_place ones at least. */
239
240   /* Note: If sublookup is reverse, it will underflow after the first loop
241    * and we jump out of it.  Not entirely disastrous.  So we don't check
242    * for reverse lookup here.
243    */
244   for (unsigned int i = 0; i < count; /* NOP */)
245   {
246     while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[c->buffer->i], c->lookup_props, NULL))
247     {
248       if (unlikely (c->buffer->i == end))
249         return true;
250       /* No lookup applied for this index */
251       c->buffer->next_glyph ();
252     }
253
254     if (lookupCount && i == lookupRecord->sequenceIndex)
255     {
256       unsigned int old_pos = c->buffer->i;
257
258       /* Apply a lookup */
259       bool done = apply_func (c, lookupRecord->lookupListIndex);
260
261       lookupRecord++;
262       lookupCount--;
263       /* Err, this is wrong if the lookup jumped over some glyphs */
264       i += c->buffer->i - old_pos;
265       if (unlikely (c->buffer->i == end))
266         return true;
267
268       if (!done)
269         goto not_applied;
270     }
271     else
272     {
273     not_applied:
274       /* No lookup applied for this index */
275       c->buffer->next_glyph ();
276       i++;
277     }
278   }
279
280   return true;
281 }
282
283 HB_END_DECLS
284
285
286 /* Contextual lookups */
287
288 struct ContextLookupContext
289 {
290   ContextFuncs funcs;
291   const void *match_data;
292 };
293
294 static inline bool context_lookup (hb_apply_context_t *c,
295                                    unsigned int inputCount, /* Including the first glyph (not matched) */
296                                    const USHORT input[], /* Array of input values--start with second glyph */
297                                    unsigned int lookupCount,
298                                    const LookupRecord lookupRecord[],
299                                    ContextLookupContext &lookup_context)
300 {
301   hb_apply_context_t new_context = *c;
302   return match_input (c,
303                       inputCount, input,
304                       lookup_context.funcs.match, lookup_context.match_data,
305                       &new_context.context_length)
306       && apply_lookup (&new_context,
307                        inputCount,
308                        lookupCount, lookupRecord,
309                        lookup_context.funcs.apply);
310 }
311
312 struct Rule
313 {
314   friend struct RuleSet;
315
316   private:
317   inline bool apply (hb_apply_context_t *c, ContextLookupContext &lookup_context) const
318   {
319     TRACE_APPLY ();
320     const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (input, input[0].static_size * (inputCount ? inputCount - 1 : 0));
321     return context_lookup (c,
322                            inputCount, input,
323                            lookupCount, lookupRecord,
324                            lookup_context);
325   }
326
327   public:
328   inline bool sanitize (hb_sanitize_context_t *c) {
329     TRACE_SANITIZE ();
330     return inputCount.sanitize (c)
331         && lookupCount.sanitize (c)
332         && c->check_range (input,
333                                  input[0].static_size * inputCount
334                                  + lookupRecordX[0].static_size * lookupCount);
335   }
336
337   private:
338   USHORT        inputCount;             /* Total number of glyphs in input
339                                          * glyph sequence--includes the  first
340                                          * glyph */
341   USHORT        lookupCount;            /* Number of LookupRecords */
342   USHORT        input[VAR];             /* Array of match inputs--start with
343                                          * second glyph */
344   LookupRecord  lookupRecordX[VAR];     /* Array of LookupRecords--in
345                                          * design order */
346   public:
347   DEFINE_SIZE_ARRAY2 (4, input, lookupRecordX);
348 };
349
350 struct RuleSet
351 {
352   inline bool apply (hb_apply_context_t *c, ContextLookupContext &lookup_context) const
353   {
354     TRACE_APPLY ();
355     unsigned int num_rules = rule.len;
356     for (unsigned int i = 0; i < num_rules; i++)
357     {
358       if ((this+rule[i]).apply (c, lookup_context))
359         return true;
360     }
361
362     return false;
363   }
364
365   inline bool sanitize (hb_sanitize_context_t *c) {
366     TRACE_SANITIZE ();
367     return rule.sanitize (c, this);
368   }
369
370   private:
371   OffsetArrayOf<Rule>
372                 rule;                   /* Array of Rule tables
373                                          * ordered by preference */
374   public:
375   DEFINE_SIZE_ARRAY (2, rule);
376 };
377
378
379 struct ContextFormat1
380 {
381   friend struct Context;
382
383   private:
384   inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
385   {
386     TRACE_APPLY ();
387     unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoint);
388     if (likely (index == NOT_COVERED))
389       return false;
390
391     const RuleSet &rule_set = this+ruleSet[index];
392     struct ContextLookupContext lookup_context = {
393       {match_glyph, apply_func},
394       NULL
395     };
396     return rule_set.apply (c, lookup_context);
397   }
398
399   inline bool sanitize (hb_sanitize_context_t *c) {
400     TRACE_SANITIZE ();
401     return coverage.sanitize (c, this)
402         && ruleSet.sanitize (c, this);
403   }
404
405   private:
406   USHORT        format;                 /* Format identifier--format = 1 */
407   OffsetTo<Coverage>
408                 coverage;               /* Offset to Coverage table--from
409                                          * beginning of table */
410   OffsetArrayOf<RuleSet>
411                 ruleSet;                /* Array of RuleSet tables
412                                          * ordered by Coverage Index */
413   public:
414   DEFINE_SIZE_ARRAY (6, ruleSet);
415 };
416
417
418 struct ContextFormat2
419 {
420   friend struct Context;
421
422   private:
423   inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
424   {
425     TRACE_APPLY ();
426     unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoint);
427     if (likely (index == NOT_COVERED))
428       return false;
429
430     const ClassDef &class_def = this+classDef;
431     index = class_def (c->buffer->info[c->buffer->i].codepoint);
432     const RuleSet &rule_set = this+ruleSet[index];
433     struct ContextLookupContext lookup_context = {
434       {match_class, apply_func},
435       &class_def
436     };
437     return rule_set.apply (c, lookup_context);
438   }
439
440   inline bool sanitize (hb_sanitize_context_t *c) {
441     TRACE_SANITIZE ();
442     return coverage.sanitize (c, this)
443         && classDef.sanitize (c, this)
444         && ruleSet.sanitize (c, this);
445   }
446
447   private:
448   USHORT        format;                 /* Format identifier--format = 2 */
449   OffsetTo<Coverage>
450                 coverage;               /* Offset to Coverage table--from
451                                          * beginning of table */
452   OffsetTo<ClassDef>
453                 classDef;               /* Offset to glyph ClassDef table--from
454                                          * beginning of table */
455   OffsetArrayOf<RuleSet>
456                 ruleSet;                /* Array of RuleSet tables
457                                          * ordered by class */
458   public:
459   DEFINE_SIZE_ARRAY (8, ruleSet);
460 };
461
462
463 struct ContextFormat3
464 {
465   friend struct Context;
466
467   private:
468   inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
469   {
470     TRACE_APPLY ();
471     unsigned int index = (this+coverage[0]) (c->buffer->info[c->buffer->i].codepoint);
472     if (likely (index == NOT_COVERED))
473       return false;
474
475     const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].static_size * glyphCount);
476     struct ContextLookupContext lookup_context = {
477       {match_coverage, apply_func},
478       this
479     };
480     return context_lookup (c,
481                            glyphCount, (const USHORT *) (coverage + 1),
482                            lookupCount, lookupRecord,
483                            lookup_context);
484   }
485
486   inline bool sanitize (hb_sanitize_context_t *c) {
487     TRACE_SANITIZE ();
488     if (!c->check_struct (this)) return false;
489     unsigned int count = glyphCount;
490     if (!c->check_array (coverage, coverage[0].static_size, count)) return false;
491     for (unsigned int i = 0; i < count; i++)
492       if (!coverage[i].sanitize (c, this)) return false;
493     LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].static_size * count);
494     return c->check_array (lookupRecord, lookupRecord[0].static_size, lookupCount);
495   }
496
497   private:
498   USHORT        format;                 /* Format identifier--format = 3 */
499   USHORT        glyphCount;             /* Number of glyphs in the input glyph
500                                          * sequence */
501   USHORT        lookupCount;            /* Number of LookupRecords */
502   OffsetTo<Coverage>
503                 coverage[VAR];          /* Array of offsets to Coverage
504                                          * table in glyph sequence order */
505   LookupRecord  lookupRecordX[VAR];     /* Array of LookupRecords--in
506                                          * design order */
507   public:
508   DEFINE_SIZE_ARRAY2 (6, coverage, lookupRecordX);
509 };
510
511 struct Context
512 {
513   protected:
514   inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
515   {
516     TRACE_APPLY ();
517     switch (u.format) {
518     case 1: return u.format1.apply (c, apply_func);
519     case 2: return u.format2.apply (c, apply_func);
520     case 3: return u.format3.apply (c, apply_func);
521     default:return false;
522     }
523   }
524
525   inline bool sanitize (hb_sanitize_context_t *c) {
526     TRACE_SANITIZE ();
527     if (!u.format.sanitize (c)) return false;
528     switch (u.format) {
529     case 1: return u.format1.sanitize (c);
530     case 2: return u.format2.sanitize (c);
531     case 3: return u.format3.sanitize (c);
532     default:return true;
533     }
534   }
535
536   private:
537   union {
538   USHORT                format;         /* Format identifier */
539   ContextFormat1        format1;
540   ContextFormat2        format2;
541   ContextFormat3        format3;
542   } u;
543 };
544
545
546 /* Chaining Contextual lookups */
547
548 struct ChainContextLookupContext
549 {
550   ContextFuncs funcs;
551   const void *match_data[3];
552 };
553
554 static inline bool chain_context_lookup (hb_apply_context_t *c,
555                                          unsigned int backtrackCount,
556                                          const USHORT backtrack[],
557                                          unsigned int inputCount, /* Including the first glyph (not matched) */
558                                          const USHORT input[], /* Array of input values--start with second glyph */
559                                          unsigned int lookaheadCount,
560                                          const USHORT lookahead[],
561                                          unsigned int lookupCount,
562                                          const LookupRecord lookupRecord[],
563                                          ChainContextLookupContext &lookup_context)
564 {
565   /* First guess */
566   if (unlikely (c->buffer->backtrack_len () < backtrackCount ||
567                 c->buffer->i + inputCount + lookaheadCount > c->buffer->len ||
568                 inputCount + lookaheadCount > c->context_length))
569     return false;
570
571   hb_apply_context_t new_context = *c;
572   return match_backtrack (c,
573                           backtrackCount, backtrack,
574                           lookup_context.funcs.match, lookup_context.match_data[0])
575       && match_input (c,
576                       inputCount, input,
577                       lookup_context.funcs.match, lookup_context.match_data[1],
578                       &new_context.context_length)
579       && match_lookahead (c,
580                           lookaheadCount, lookahead,
581                           lookup_context.funcs.match, lookup_context.match_data[2],
582                           new_context.context_length)
583       && apply_lookup (&new_context,
584                        inputCount,
585                        lookupCount, lookupRecord,
586                        lookup_context.funcs.apply);
587 }
588
589 struct ChainRule
590 {
591   friend struct ChainRuleSet;
592
593   private:
594   inline bool apply (hb_apply_context_t *c, ChainContextLookupContext &lookup_context) const
595   {
596     TRACE_APPLY ();
597     const HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack);
598     const ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input);
599     const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
600     return chain_context_lookup (c,
601                                  backtrack.len, backtrack.array,
602                                  input.len, input.array,
603                                  lookahead.len, lookahead.array,
604                                  lookup.len, lookup.array,
605                                  lookup_context);
606   }
607
608   public:
609   inline bool sanitize (hb_sanitize_context_t *c) {
610     TRACE_SANITIZE ();
611     if (!backtrack.sanitize (c)) return false;
612     HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack);
613     if (!input.sanitize (c)) return false;
614     ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input);
615     if (!lookahead.sanitize (c)) return false;
616     ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
617     return lookup.sanitize (c);
618   }
619
620   private:
621   ArrayOf<USHORT>
622                 backtrack;              /* Array of backtracking values
623                                          * (to be matched before the input
624                                          * sequence) */
625   HeadlessArrayOf<USHORT>
626                 inputX;                 /* Array of input values (start with
627                                          * second glyph) */
628   ArrayOf<USHORT>
629                 lookaheadX;             /* Array of lookahead values's (to be
630                                          * matched after the input sequence) */
631   ArrayOf<LookupRecord>
632                 lookupX;                /* Array of LookupRecords--in
633                                          * design order) */
634   public:
635   DEFINE_SIZE_MIN (8);
636 };
637
638 struct ChainRuleSet
639 {
640   inline bool apply (hb_apply_context_t *c, ChainContextLookupContext &lookup_context) const
641   {
642     TRACE_APPLY ();
643     unsigned int num_rules = rule.len;
644     for (unsigned int i = 0; i < num_rules; i++)
645     {
646       if ((this+rule[i]).apply (c, lookup_context))
647         return true;
648     }
649
650     return false;
651   }
652
653   inline bool sanitize (hb_sanitize_context_t *c) {
654     TRACE_SANITIZE ();
655     return rule.sanitize (c, this);
656   }
657
658   private:
659   OffsetArrayOf<ChainRule>
660                 rule;                   /* Array of ChainRule tables
661                                          * ordered by preference */
662   public:
663   DEFINE_SIZE_ARRAY (2, rule);
664 };
665
666 struct ChainContextFormat1
667 {
668   friend struct ChainContext;
669
670   private:
671   inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
672   {
673     TRACE_APPLY ();
674     unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoint);
675     if (likely (index == NOT_COVERED))
676       return false;
677
678     const ChainRuleSet &rule_set = this+ruleSet[index];
679     struct ChainContextLookupContext lookup_context = {
680       {match_glyph, apply_func},
681       {NULL, NULL, NULL}
682     };
683     return rule_set.apply (c, lookup_context);
684   }
685
686   inline bool sanitize (hb_sanitize_context_t *c) {
687     TRACE_SANITIZE ();
688     return coverage.sanitize (c, this)
689         && ruleSet.sanitize (c, this);
690   }
691
692   private:
693   USHORT        format;                 /* Format identifier--format = 1 */
694   OffsetTo<Coverage>
695                 coverage;               /* Offset to Coverage table--from
696                                          * beginning of table */
697   OffsetArrayOf<ChainRuleSet>
698                 ruleSet;                /* Array of ChainRuleSet tables
699                                          * ordered by Coverage Index */
700   public:
701   DEFINE_SIZE_ARRAY (6, ruleSet);
702 };
703
704 struct ChainContextFormat2
705 {
706   friend struct ChainContext;
707
708   private:
709   inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
710   {
711     TRACE_APPLY ();
712     unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoint);
713     if (likely (index == NOT_COVERED))
714       return false;
715
716     const ClassDef &backtrack_class_def = this+backtrackClassDef;
717     const ClassDef &input_class_def = this+inputClassDef;
718     const ClassDef &lookahead_class_def = this+lookaheadClassDef;
719
720     index = input_class_def (c->buffer->info[c->buffer->i].codepoint);
721     const ChainRuleSet &rule_set = this+ruleSet[index];
722     struct ChainContextLookupContext lookup_context = {
723       {match_class, apply_func},
724       {&backtrack_class_def,
725        &input_class_def,
726        &lookahead_class_def}
727     };
728     return rule_set.apply (c, lookup_context);
729   }
730
731   inline bool sanitize (hb_sanitize_context_t *c) {
732     TRACE_SANITIZE ();
733     return coverage.sanitize (c, this)
734         && backtrackClassDef.sanitize (c, this)
735         && inputClassDef.sanitize (c, this)
736         && lookaheadClassDef.sanitize (c, this)
737         && ruleSet.sanitize (c, this);
738   }
739
740   private:
741   USHORT        format;                 /* Format identifier--format = 2 */
742   OffsetTo<Coverage>
743                 coverage;               /* Offset to Coverage table--from
744                                          * beginning of table */
745   OffsetTo<ClassDef>
746                 backtrackClassDef;      /* Offset to glyph ClassDef table
747                                          * containing backtrack sequence
748                                          * data--from beginning of table */
749   OffsetTo<ClassDef>
750                 inputClassDef;          /* Offset to glyph ClassDef
751                                          * table containing input sequence
752                                          * data--from beginning of table */
753   OffsetTo<ClassDef>
754                 lookaheadClassDef;      /* Offset to glyph ClassDef table
755                                          * containing lookahead sequence
756                                          * data--from beginning of table */
757   OffsetArrayOf<ChainRuleSet>
758                 ruleSet;                /* Array of ChainRuleSet tables
759                                          * ordered by class */
760   public:
761   DEFINE_SIZE_ARRAY (12, ruleSet);
762 };
763
764 struct ChainContextFormat3
765 {
766   friend struct ChainContext;
767
768   private:
769
770   inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
771   {
772     TRACE_APPLY ();
773     const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
774
775     unsigned int index = (this+input[0]) (c->buffer->info[c->buffer->i].codepoint);
776     if (likely (index == NOT_COVERED))
777       return false;
778
779     const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (input);
780     const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
781     struct ChainContextLookupContext lookup_context = {
782       {match_coverage, apply_func},
783       {this, this, this}
784     };
785     return chain_context_lookup (c,
786                                  backtrack.len, (const USHORT *) backtrack.array,
787                                  input.len, (const USHORT *) input.array + 1,
788                                  lookahead.len, (const USHORT *) lookahead.array,
789                                  lookup.len, lookup.array,
790                                  lookup_context);
791   }
792
793   inline bool sanitize (hb_sanitize_context_t *c) {
794     TRACE_SANITIZE ();
795     if (!backtrack.sanitize (c, this)) return false;
796     OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
797     if (!input.sanitize (c, this)) return false;
798     OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (input);
799     if (!lookahead.sanitize (c, this)) return false;
800     ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
801     return lookup.sanitize (c);
802   }
803
804   private:
805   USHORT        format;                 /* Format identifier--format = 3 */
806   OffsetArrayOf<Coverage>
807                 backtrack;              /* Array of coverage tables
808                                          * in backtracking sequence, in  glyph
809                                          * sequence order */
810   OffsetArrayOf<Coverage>
811                 inputX          ;       /* Array of coverage
812                                          * tables in input sequence, in glyph
813                                          * sequence order */
814   OffsetArrayOf<Coverage>
815                 lookaheadX;             /* Array of coverage tables
816                                          * in lookahead sequence, in glyph
817                                          * sequence order */
818   ArrayOf<LookupRecord>
819                 lookupX;                /* Array of LookupRecords--in
820                                          * design order) */
821   public:
822   DEFINE_SIZE_MIN (10);
823 };
824
825 struct ChainContext
826 {
827   protected:
828   inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
829   {
830     TRACE_APPLY ();
831     switch (u.format) {
832     case 1: return u.format1.apply (c, apply_func);
833     case 2: return u.format2.apply (c, apply_func);
834     case 3: return u.format3.apply (c, apply_func);
835     default:return false;
836     }
837   }
838
839   inline bool sanitize (hb_sanitize_context_t *c) {
840     TRACE_SANITIZE ();
841     if (!u.format.sanitize (c)) return false;
842     switch (u.format) {
843     case 1: return u.format1.sanitize (c);
844     case 2: return u.format2.sanitize (c);
845     case 3: return u.format3.sanitize (c);
846     default:return true;
847     }
848   }
849
850   private:
851   union {
852   USHORT                format; /* Format identifier */
853   ChainContextFormat1   format1;
854   ChainContextFormat2   format2;
855   ChainContextFormat3   format3;
856   } u;
857 };
858
859
860 struct ExtensionFormat1
861 {
862   friend struct Extension;
863
864   protected:
865   inline unsigned int get_type (void) const { return extensionLookupType; }
866   inline unsigned int get_offset (void) const { return extensionOffset; }
867
868   inline bool sanitize (hb_sanitize_context_t *c) {
869     TRACE_SANITIZE ();
870     return c->check_struct (this);
871   }
872
873   private:
874   USHORT        format;                 /* Format identifier. Set to 1. */
875   USHORT        extensionLookupType;    /* Lookup type of subtable referenced
876                                          * by ExtensionOffset (i.e. the
877                                          * extension subtable). */
878   ULONG         extensionOffset;        /* Offset to the extension subtable,
879                                          * of lookup type subtable. */
880   public:
881   DEFINE_SIZE_STATIC (8);
882 };
883
884 struct Extension
885 {
886   inline unsigned int get_type (void) const
887   {
888     switch (u.format) {
889     case 1: return u.format1.get_type ();
890     default:return 0;
891     }
892   }
893   inline unsigned int get_offset (void) const
894   {
895     switch (u.format) {
896     case 1: return u.format1.get_offset ();
897     default:return 0;
898     }
899   }
900
901   inline bool sanitize (hb_sanitize_context_t *c) {
902     TRACE_SANITIZE ();
903     if (!u.format.sanitize (c)) return false;
904     switch (u.format) {
905     case 1: return u.format1.sanitize (c);
906     default:return true;
907     }
908   }
909
910   private:
911   union {
912   USHORT                format;         /* Format identifier */
913   ExtensionFormat1      format1;
914   } u;
915 };
916
917
918 /*
919  * GSUB/GPOS Common
920  */
921
922 struct GSUBGPOS
923 {
924   static const hb_tag_t GSUBTag = HB_OT_TAG_GSUB;
925   static const hb_tag_t GPOSTag = HB_OT_TAG_GPOS;
926
927   inline unsigned int get_script_count (void) const
928   { return (this+scriptList).len; }
929   inline const Tag& get_script_tag (unsigned int i) const
930   { return (this+scriptList).get_tag (i); }
931   inline unsigned int get_script_tags (unsigned int start_offset,
932                                        unsigned int *script_count /* IN/OUT */,
933                                        hb_tag_t     *script_tags /* OUT */) const
934   { return (this+scriptList).get_tags (start_offset, script_count, script_tags); }
935   inline const Script& get_script (unsigned int i) const
936   { return (this+scriptList)[i]; }
937   inline bool find_script_index (hb_tag_t tag, unsigned int *index) const
938   { return (this+scriptList).find_index (tag, index); }
939
940   inline unsigned int get_feature_count (void) const
941   { return (this+featureList).len; }
942   inline const Tag& get_feature_tag (unsigned int i) const
943   { return (this+featureList).get_tag (i); }
944   inline unsigned int get_feature_tags (unsigned int start_offset,
945                                         unsigned int *feature_count /* IN/OUT */,
946                                         hb_tag_t     *feature_tags /* OUT */) const
947   { return (this+featureList).get_tags (start_offset, feature_count, feature_tags); }
948   inline const Feature& get_feature (unsigned int i) const
949   { return (this+featureList)[i]; }
950   inline bool find_feature_index (hb_tag_t tag, unsigned int *index) const
951   { return (this+featureList).find_index (tag, index); }
952
953   inline unsigned int get_lookup_count (void) const
954   { return (this+lookupList).len; }
955   inline const Lookup& get_lookup (unsigned int i) const
956   { return (this+lookupList)[i]; }
957
958   inline bool sanitize (hb_sanitize_context_t *c) {
959     TRACE_SANITIZE ();
960     return version.sanitize (c) && likely (version.major == 1)
961         && scriptList.sanitize (c, this)
962         && featureList.sanitize (c, this)
963         && lookupList.sanitize (c, this);
964   }
965
966   protected:
967   FixedVersion  version;        /* Version of the GSUB/GPOS table--initially set
968                                  * to 0x00010000 */
969   OffsetTo<ScriptList>
970                 scriptList;     /* ScriptList table */
971   OffsetTo<FeatureList>
972                 featureList;    /* FeatureList table */
973   OffsetTo<LookupList>
974                 lookupList;     /* LookupList table */
975   public:
976   DEFINE_SIZE_STATIC (10);
977 };
978
979
980 HB_END_DECLS
981
982 #endif /* HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH */