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