hb-ot-layout-gsubgpos-private.hh revision b961518b9611471ff7060e97686e5625974847eb
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.h"
31#include "hb-ot-layout-gdef-private.hh"
32
33
34#ifndef HB_DEBUG_APPLY
35#define HB_DEBUG_APPLY HB_DEBUG+0
36#endif
37
38#define TRACE_APPLY() \
39	hb_trace_t<HB_DEBUG_APPLY> trace (&context->debug_depth); \
40	trace.log ("APPLY", HB_FUNC, this);
41
42
43struct hb_apply_context_t
44{
45  unsigned int debug_depth;
46  hb_ot_layout_context_t *layout;
47  hb_buffer_t *buffer;
48  unsigned int context_length;
49  unsigned int nesting_level_left;
50  unsigned int lookup_flag;
51  unsigned int property; /* propety of first glyph (TODO remove) */
52};
53
54
55
56
57#undef BUFFER
58#define BUFFER context->buffer
59
60
61typedef bool (*match_func_t) (hb_codepoint_t glyph_id, const USHORT &value, const void *data);
62typedef bool (*apply_lookup_func_t) (hb_apply_context_t *context, unsigned int lookup_index);
63
64struct ContextFuncs
65{
66  match_func_t match;
67  apply_lookup_func_t apply;
68};
69
70
71static inline bool match_glyph (hb_codepoint_t glyph_id, const USHORT &value, const void *data HB_UNUSED)
72{
73  return glyph_id == value;
74}
75
76static inline bool match_class (hb_codepoint_t glyph_id, const USHORT &value, const void *data)
77{
78  const ClassDef &class_def = *reinterpret_cast<const ClassDef *>(data);
79  return class_def.get_class (glyph_id) == value;
80}
81
82static inline bool match_coverage (hb_codepoint_t glyph_id, const USHORT &value, const void *data)
83{
84  const OffsetTo<Coverage> &coverage = (const OffsetTo<Coverage>&)value;
85  return (data+coverage) (glyph_id) != NOT_COVERED;
86}
87
88
89static inline bool match_input (hb_apply_context_t *context,
90				unsigned int count, /* Including the first glyph (not matched) */
91				const USHORT input[], /* Array of input values--start with second glyph */
92				match_func_t match_func,
93				const void *match_data,
94				unsigned int *context_length_out)
95{
96  unsigned int i, j;
97  unsigned int end = MIN (context->buffer->in_length, context->buffer->in_pos + context->context_length);
98  if (unlikely (context->buffer->in_pos + count > end))
99    return false;
100
101  for (i = 1, j = context->buffer->in_pos + 1; i < count; i++, j++)
102  {
103    while (_hb_ot_layout_skip_mark (context->layout->face, IN_INFO (j), context->lookup_flag, NULL))
104    {
105      if (unlikely (j + count - i == end))
106	return false;
107      j++;
108    }
109
110    if (likely (!match_func (IN_GLYPH (j), input[i - 1], match_data)))
111      return false;
112  }
113
114  *context_length_out = j - context->buffer->in_pos;
115
116  return true;
117}
118
119static inline bool match_backtrack (hb_apply_context_t *context,
120				    unsigned int count,
121				    const USHORT backtrack[],
122				    match_func_t match_func,
123				    const void *match_data)
124{
125  if (unlikely (context->buffer->out_pos < count))
126    return false;
127
128  for (unsigned int i = 0, j = context->buffer->out_pos - 1; i < count; i++, j--)
129  {
130    while (_hb_ot_layout_skip_mark (context->layout->face, OUT_INFO (j), context->lookup_flag, NULL))
131    {
132      if (unlikely (j + 1 == count - i))
133	return false;
134      j--;
135    }
136
137    if (likely (!match_func (OUT_GLYPH (j), backtrack[i], match_data)))
138      return false;
139  }
140
141  return true;
142}
143
144static inline bool match_lookahead (hb_apply_context_t *context,
145				    unsigned int count,
146				    const USHORT lookahead[],
147				    match_func_t match_func,
148				    const void *match_data,
149				    unsigned int offset)
150{
151  unsigned int i, j;
152  unsigned int end = MIN (context->buffer->in_length, context->buffer->in_pos + context->context_length);
153  if (unlikely (context->buffer->in_pos + offset + count > end))
154    return false;
155
156  for (i = 0, j = context->buffer->in_pos + offset; i < count; i++, j++)
157  {
158    while (_hb_ot_layout_skip_mark (context->layout->face, OUT_INFO (j), context->lookup_flag, NULL))
159    {
160      if (unlikely (j + count - i == end))
161	return false;
162      j++;
163    }
164
165    if (likely (!match_func (IN_GLYPH (j), lookahead[i], match_data)))
166      return false;
167  }
168
169  return true;
170}
171
172
173struct LookupRecord
174{
175  inline bool sanitize (hb_sanitize_context_t *context) {
176    TRACE_SANITIZE ();
177    return context->check_struct (this);
178  }
179
180  USHORT	sequenceIndex;		/* Index into current glyph
181					 * sequence--first glyph = 0 */
182  USHORT	lookupListIndex;	/* Lookup to apply to that
183					 * position--zero--based */
184  public:
185  DEFINE_SIZE_STATIC (4);
186};
187
188static inline bool apply_lookup (hb_apply_context_t *context,
189				 unsigned int count, /* Including the first glyph */
190				 unsigned int lookupCount,
191				 const LookupRecord lookupRecord[], /* Array of LookupRecords--in design order */
192				 apply_lookup_func_t apply_func)
193{
194  unsigned int end = MIN (context->buffer->in_length, context->buffer->in_pos + context->context_length);
195  if (unlikely (context->buffer->in_pos + count > end))
196    return false;
197
198  /* TODO We don't support lookupRecord arrays that are not increasing:
199   *      Should be easy for in_place ones at least. */
200
201  /* Note: If sublookup is reverse, i will underflow after the first loop
202   * and we jump out of it.  Not entirely disastrous.  So we don't check
203   * for reverse lookup here.
204   */
205  for (unsigned int i = 0; i < count; /* NOP */)
206  {
207    while (_hb_ot_layout_skip_mark (context->layout->face, IN_CURINFO (), context->lookup_flag, NULL))
208    {
209      if (unlikely (context->buffer->in_pos == end))
210	return true;
211      /* No lookup applied for this index */
212      _hb_buffer_next_glyph (context->buffer);
213    }
214
215    if (lookupCount && i == lookupRecord->sequenceIndex)
216    {
217      unsigned int old_pos = context->buffer->in_pos;
218
219      /* Apply a lookup */
220      bool done = apply_func (context, lookupRecord->lookupListIndex);
221
222      lookupRecord++;
223      lookupCount--;
224      /* Err, this is wrong if the lookup jumped over some glyphs */
225      i += context->buffer->in_pos - old_pos;
226      if (unlikely (context->buffer->in_pos == end))
227	return true;
228
229      if (!done)
230	goto not_applied;
231    }
232    else
233    {
234    not_applied:
235      /* No lookup applied for this index */
236      _hb_buffer_next_glyph (context->buffer);
237      i++;
238    }
239  }
240
241  return true;
242}
243
244
245/* Contextual lookups */
246
247struct ContextLookupContext
248{
249  ContextFuncs funcs;
250  const void *match_data;
251};
252
253static inline bool context_lookup (hb_apply_context_t *context,
254				   unsigned int inputCount, /* Including the first glyph (not matched) */
255				   const USHORT input[], /* Array of input values--start with second glyph */
256				   unsigned int lookupCount,
257				   const LookupRecord lookupRecord[],
258				   ContextLookupContext &lookup_context)
259{
260  hb_apply_context_t new_context = *context;
261  return match_input (context,
262		      inputCount, input,
263		      lookup_context.funcs.match, lookup_context.match_data,
264		      &new_context.context_length)
265      && apply_lookup (&new_context,
266		       inputCount,
267		       lookupCount, lookupRecord,
268		       lookup_context.funcs.apply);
269}
270
271struct Rule
272{
273  friend struct RuleSet;
274
275  private:
276  inline bool apply (hb_apply_context_t *context, ContextLookupContext &lookup_context) const
277  {
278    TRACE_APPLY ();
279    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (input, input[0].static_size * (inputCount ? inputCount - 1 : 0));
280    return context_lookup (context,
281			   inputCount, input,
282			   lookupCount, lookupRecord,
283			   lookup_context);
284  }
285
286  public:
287  inline bool sanitize (hb_sanitize_context_t *context) {
288    TRACE_SANITIZE ();
289    return inputCount.sanitize (context)
290	&& lookupCount.sanitize (context)
291	&& context->check_range (input,
292				 input[0].static_size * inputCount
293				 + lookupRecordX[0].static_size * lookupCount);
294  }
295
296  private:
297  USHORT	inputCount;		/* Total number of glyphs in input
298					 * glyph sequence--includes the  first
299					 * glyph */
300  USHORT	lookupCount;		/* Number of LookupRecords */
301  USHORT	input[VAR];		/* Array of match inputs--start with
302					 * second glyph */
303  LookupRecord	lookupRecordX[VAR];	/* Array of LookupRecords--in
304					 * design order */
305  public:
306  DEFINE_SIZE_VAR2 (4, USHORT, LookupRecord);
307};
308
309struct RuleSet
310{
311  inline bool apply (hb_apply_context_t *context, ContextLookupContext &lookup_context) const
312  {
313    TRACE_APPLY ();
314    unsigned int num_rules = rule.len;
315    for (unsigned int i = 0; i < num_rules; i++)
316    {
317      if ((this+rule[i]).apply (context, lookup_context))
318        return true;
319    }
320
321    return false;
322  }
323
324  inline bool sanitize (hb_sanitize_context_t *context) {
325    TRACE_SANITIZE ();
326    return rule.sanitize (context, this);
327  }
328
329  private:
330  OffsetArrayOf<Rule>
331		rule;			/* Array of Rule tables
332					 * ordered by preference */
333  public:
334  DEFINE_SIZE_VAR (2, OffsetTo<Rule>);
335};
336
337
338struct ContextFormat1
339{
340  friend struct Context;
341
342  private:
343  inline bool apply (hb_apply_context_t *context, apply_lookup_func_t apply_func) const
344  {
345    TRACE_APPLY ();
346    unsigned int index = (this+coverage) (IN_CURGLYPH ());
347    if (likely (index == NOT_COVERED))
348      return false;
349
350    const RuleSet &rule_set = this+ruleSet[index];
351    struct ContextLookupContext lookup_context = {
352      {match_glyph, apply_func},
353      NULL
354    };
355    return rule_set.apply (context, lookup_context);
356  }
357
358  inline bool sanitize (hb_sanitize_context_t *context) {
359    TRACE_SANITIZE ();
360    return coverage.sanitize (context, this)
361	&& ruleSet.sanitize (context, this);
362  }
363
364  private:
365  USHORT	format;			/* Format identifier--format = 1 */
366  OffsetTo<Coverage>
367		coverage;		/* Offset to Coverage table--from
368					 * beginning of table */
369  OffsetArrayOf<RuleSet>
370		ruleSet;		/* Array of RuleSet tables
371					 * ordered by Coverage Index */
372  public:
373  DEFINE_SIZE_VAR (6, OffsetTo<RuleSet>);
374};
375
376
377struct ContextFormat2
378{
379  friend struct Context;
380
381  private:
382  inline bool apply (hb_apply_context_t *context, apply_lookup_func_t apply_func) const
383  {
384    TRACE_APPLY ();
385    unsigned int index = (this+coverage) (IN_CURGLYPH ());
386    if (likely (index == NOT_COVERED))
387      return false;
388
389    const ClassDef &class_def = this+classDef;
390    index = class_def (IN_CURGLYPH ());
391    const RuleSet &rule_set = this+ruleSet[index];
392    /* LONGTERMTODO: Old code fetches glyph classes at most once and caches
393     * them across subrule lookups.  Not sure it's worth it.
394     */
395    struct ContextLookupContext lookup_context = {
396      {match_class, apply_func},
397      &class_def
398    };
399    return rule_set.apply (context, lookup_context);
400  }
401
402  inline bool sanitize (hb_sanitize_context_t *context) {
403    TRACE_SANITIZE ();
404    return coverage.sanitize (context, this)
405        && classDef.sanitize (context, this)
406	&& ruleSet.sanitize (context, this);
407  }
408
409  private:
410  USHORT	format;			/* Format identifier--format = 2 */
411  OffsetTo<Coverage>
412		coverage;		/* Offset to Coverage table--from
413					 * beginning of table */
414  OffsetTo<ClassDef>
415		classDef;		/* Offset to glyph ClassDef table--from
416					 * beginning of table */
417  OffsetArrayOf<RuleSet>
418		ruleSet;		/* Array of RuleSet tables
419					 * ordered by class */
420  public:
421  DEFINE_SIZE_VAR (8, OffsetTo<RuleSet>);
422};
423
424
425struct ContextFormat3
426{
427  friend struct Context;
428
429  private:
430  inline bool apply (hb_apply_context_t *context, apply_lookup_func_t apply_func) const
431  {
432    TRACE_APPLY ();
433    unsigned int index = (this+coverage[0]) (IN_CURGLYPH ());
434    if (likely (index == NOT_COVERED))
435      return false;
436
437    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].static_size * glyphCount);
438    struct ContextLookupContext lookup_context = {
439      {match_coverage, apply_func},
440      this
441    };
442    return context_lookup (context,
443			   glyphCount, (const USHORT *) (coverage + 1),
444			   lookupCount, lookupRecord,
445			   lookup_context);
446  }
447
448  inline bool sanitize (hb_sanitize_context_t *context) {
449    TRACE_SANITIZE ();
450    if (!context->check_struct (this)) return false;
451    unsigned int count = glyphCount;
452    if (!context->check_array (coverage, coverage[0].static_size, count)) return false;
453    for (unsigned int i = 0; i < count; i++)
454      if (!coverage[i].sanitize (context, this)) return false;
455    LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].static_size * count);
456    return context->check_array (lookupRecord, lookupRecord[0].static_size, lookupCount);
457  }
458
459  private:
460  USHORT	format;			/* Format identifier--format = 3 */
461  USHORT	glyphCount;		/* Number of glyphs in the input glyph
462					 * sequence */
463  USHORT	lookupCount;		/* Number of LookupRecords */
464  OffsetTo<Coverage>
465		coverage[VAR];		/* Array of offsets to Coverage
466					 * table in glyph sequence order */
467  LookupRecord	lookupRecordX[VAR];	/* Array of LookupRecords--in
468					 * design order */
469  public:
470  DEFINE_SIZE_VAR2 (6, OffsetTo<Coverage>, LookupRecord);
471};
472
473struct Context
474{
475  protected:
476  inline bool apply (hb_apply_context_t *context, apply_lookup_func_t apply_func) const
477  {
478    TRACE_APPLY ();
479    switch (u.format) {
480    case 1: return u.format1->apply (context, apply_func);
481    case 2: return u.format2->apply (context, apply_func);
482    case 3: return u.format3->apply (context, apply_func);
483    default:return false;
484    }
485  }
486
487  inline bool sanitize (hb_sanitize_context_t *context) {
488    TRACE_SANITIZE ();
489    if (!u.format.sanitize (context)) return false;
490    switch (u.format) {
491    case 1: return u.format1->sanitize (context);
492    case 2: return u.format2->sanitize (context);
493    case 3: return u.format3->sanitize (context);
494    default:return true;
495    }
496  }
497
498  private:
499  union {
500  USHORT		format;		/* Format identifier */
501  ContextFormat1	format1[VAR];
502  ContextFormat2	format2[VAR];
503  ContextFormat3	format3[VAR];
504  } u;
505};
506
507
508/* Chaining Contextual lookups */
509
510struct ChainContextLookupContext
511{
512  ContextFuncs funcs;
513  const void *match_data[3];
514};
515
516static inline bool chain_context_lookup (hb_apply_context_t *context,
517					 unsigned int backtrackCount,
518					 const USHORT backtrack[],
519					 unsigned int inputCount, /* Including the first glyph (not matched) */
520					 const USHORT input[], /* Array of input values--start with second glyph */
521					 unsigned int lookaheadCount,
522					 const USHORT lookahead[],
523					 unsigned int lookupCount,
524					 const LookupRecord lookupRecord[],
525					 ChainContextLookupContext &lookup_context)
526{
527  /* First guess */
528  if (unlikely (context->buffer->out_pos < backtrackCount ||
529		context->buffer->in_pos + inputCount + lookaheadCount > context->buffer->in_length ||
530		inputCount + lookaheadCount > context->context_length))
531    return false;
532
533  hb_apply_context_t new_context = *context;
534  return match_backtrack (context,
535			  backtrackCount, backtrack,
536			  lookup_context.funcs.match, lookup_context.match_data[0])
537      && match_input (context,
538		      inputCount, input,
539		      lookup_context.funcs.match, lookup_context.match_data[1],
540		      &new_context.context_length)
541      && match_lookahead (context,
542			  lookaheadCount, lookahead,
543			  lookup_context.funcs.match, lookup_context.match_data[2],
544			  new_context.context_length)
545      && apply_lookup (&new_context,
546		       inputCount,
547		       lookupCount, lookupRecord,
548		       lookup_context.funcs.apply);
549}
550
551struct ChainRule
552{
553  friend struct ChainRuleSet;
554
555  private:
556  inline bool apply (hb_apply_context_t *context, ChainContextLookupContext &lookup_context) const
557  {
558    TRACE_APPLY ();
559    const HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack);
560    const ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input);
561    const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
562    return chain_context_lookup (context,
563				 backtrack.len, backtrack.array,
564				 input.len, input.array,
565				 lookahead.len, lookahead.array,
566				 lookup.len, lookup.array,
567				 lookup_context);
568    return false;
569  }
570
571  public:
572  inline bool sanitize (hb_sanitize_context_t *context) {
573    TRACE_SANITIZE ();
574    if (!backtrack.sanitize (context)) return false;
575    HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack);
576    if (!input.sanitize (context)) return false;
577    ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input);
578    if (!lookahead.sanitize (context)) return false;
579    ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
580    return lookup.sanitize (context);
581  }
582
583  private:
584  ArrayOf<USHORT>
585		backtrack;		/* Array of backtracking values
586					 * (to be matched before the input
587					 * sequence) */
588  HeadlessArrayOf<USHORT>
589		inputX;			/* Array of input values (start with
590					 * second glyph) */
591  ArrayOf<USHORT>
592		lookaheadX;		/* Array of lookahead values's (to be
593					 * matched after the input sequence) */
594  ArrayOf<LookupRecord>
595		lookupX;		/* Array of LookupRecords--in
596					 * design order) */
597  public:
598  DEFINE_SIZE_MIN (8);
599};
600
601struct ChainRuleSet
602{
603  inline bool apply (hb_apply_context_t *context, ChainContextLookupContext &lookup_context) const
604  {
605    TRACE_APPLY ();
606    unsigned int num_rules = rule.len;
607    for (unsigned int i = 0; i < num_rules; i++)
608    {
609      if ((this+rule[i]).apply (context, lookup_context))
610        return true;
611    }
612
613    return false;
614  }
615
616  inline bool sanitize (hb_sanitize_context_t *context) {
617    TRACE_SANITIZE ();
618    return rule.sanitize (context, this);
619  }
620
621  private:
622  OffsetArrayOf<ChainRule>
623		rule;			/* Array of ChainRule tables
624					 * ordered by preference */
625  public:
626  DEFINE_SIZE_VAR (2, OffsetTo<ChainRule>);
627};
628
629struct ChainContextFormat1
630{
631  friend struct ChainContext;
632
633  private:
634  inline bool apply (hb_apply_context_t *context, apply_lookup_func_t apply_func) const
635  {
636    TRACE_APPLY ();
637    unsigned int index = (this+coverage) (IN_CURGLYPH ());
638    if (likely (index == NOT_COVERED))
639      return false;
640
641    const ChainRuleSet &rule_set = this+ruleSet[index];
642    struct ChainContextLookupContext lookup_context = {
643      {match_glyph, apply_func},
644      {NULL, NULL, NULL}
645    };
646    return rule_set.apply (context, lookup_context);
647  }
648
649  inline bool sanitize (hb_sanitize_context_t *context) {
650    TRACE_SANITIZE ();
651    return coverage.sanitize (context, this)
652	&& ruleSet.sanitize (context, this);
653  }
654
655  private:
656  USHORT	format;			/* Format identifier--format = 1 */
657  OffsetTo<Coverage>
658		coverage;		/* Offset to Coverage table--from
659					 * beginning of table */
660  OffsetArrayOf<ChainRuleSet>
661		ruleSet;		/* Array of ChainRuleSet tables
662					 * ordered by Coverage Index */
663  public:
664  DEFINE_SIZE_VAR (6, OffsetTo<ChainRuleSet>);
665};
666
667struct ChainContextFormat2
668{
669  friend struct ChainContext;
670
671  private:
672  inline bool apply (hb_apply_context_t *context, apply_lookup_func_t apply_func) const
673  {
674    TRACE_APPLY ();
675    unsigned int index = (this+coverage) (IN_CURGLYPH ());
676    if (likely (index == NOT_COVERED))
677      return false;
678
679    const ClassDef &backtrack_class_def = this+backtrackClassDef;
680    const ClassDef &input_class_def = this+inputClassDef;
681    const ClassDef &lookahead_class_def = this+lookaheadClassDef;
682
683    index = input_class_def (IN_CURGLYPH ());
684    const ChainRuleSet &rule_set = this+ruleSet[index];
685    /* LONGTERMTODO: Old code fetches glyph classes at most once and caches
686     * them across subrule lookups.  Not sure it's worth it.
687     */
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 (context, lookup_context);
695  }
696
697  inline bool sanitize (hb_sanitize_context_t *context) {
698    TRACE_SANITIZE ();
699    return coverage.sanitize (context, this)
700	&& backtrackClassDef.sanitize (context, this)
701	&& inputClassDef.sanitize (context, this)
702	&& lookaheadClassDef.sanitize (context, this)
703	&& ruleSet.sanitize (context, 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_VAR (12, OffsetTo<ChainRuleSet>);
728};
729
730struct ChainContextFormat3
731{
732  friend struct ChainContext;
733
734  private:
735
736  inline bool apply (hb_apply_context_t *context, 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]) (IN_CURGLYPH ());
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 (context,
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 *context) {
761    TRACE_SANITIZE ();
762    if (!backtrack.sanitize (context, this)) return false;
763    OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
764    if (!input.sanitize (context, this)) return false;
765    OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (input);
766    if (!lookahead.sanitize (context, this)) return false;
767    ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
768    return lookup.sanitize (context);
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 *context, apply_lookup_func_t apply_func) const
796  {
797    TRACE_APPLY ();
798    switch (u.format) {
799    case 1: return u.format1->apply (context, apply_func);
800    case 2: return u.format2->apply (context, apply_func);
801    case 3: return u.format3->apply (context, apply_func);
802    default:return false;
803    }
804  }
805
806  inline bool sanitize (hb_sanitize_context_t *context) {
807    TRACE_SANITIZE ();
808    if (!u.format.sanitize (context)) return false;
809    switch (u.format) {
810    case 1: return u.format1->sanitize (context);
811    case 2: return u.format2->sanitize (context);
812    case 3: return u.format3->sanitize (context);
813    default:return true;
814    }
815  }
816
817  private:
818  union {
819  USHORT		format;	/* Format identifier */
820  ChainContextFormat1	format1[VAR];
821  ChainContextFormat2	format2[VAR];
822  ChainContextFormat3	format3[VAR];
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 *context) {
836    TRACE_SANITIZE ();
837    return context->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 *context) {
869    TRACE_SANITIZE ();
870    if (!u.format.sanitize (context)) return false;
871    switch (u.format) {
872    case 1: return u.format1->sanitize (context);
873    default:return true;
874    }
875  }
876
877  private:
878  union {
879  USHORT		format;		/* Format identifier */
880  ExtensionFormat1	format1[VAR];
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 *context) {
926    TRACE_SANITIZE ();
927    return version.sanitize (context) && likely (version.major == 1)
928	&& scriptList.sanitize (context, this)
929	&& featureList.sanitize (context, this)
930	&& lookupList.sanitize (context, 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
947#endif /* HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH */
948