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