Always allocate new ligature id
[framework/uifw/harfbuzz.git] / src / hb-ot-layout-gpos-private.hh
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_GPOS_PRIVATE_HH
28 #define HB_OT_LAYOUT_GPOS_PRIVATE_HH
29
30 #include "hb-ot-layout-gsubgpos-private.hh"
31
32 HB_BEGIN_DECLS
33
34
35 /* Shared Tables: ValueRecord, Anchor Table, and MarkArray */
36
37 typedef USHORT Value;
38
39 typedef Value ValueRecord[VAR];
40
41 struct ValueFormat : USHORT
42 {
43   enum
44   {
45     xPlacement  = 0x0001,       /* Includes horizontal adjustment for placement */
46     yPlacement  = 0x0002,       /* Includes vertical adjustment for placement */
47     xAdvance    = 0x0004,       /* Includes horizontal adjustment for advance */
48     yAdvance    = 0x0008,       /* Includes vertical adjustment for advance */
49     xPlaDevice  = 0x0010,       /* Includes horizontal Device table for placement */
50     yPlaDevice  = 0x0020,       /* Includes vertical Device table for placement */
51     xAdvDevice  = 0x0040,       /* Includes horizontal Device table for advance */
52     yAdvDevice  = 0x0080,       /* Includes vertical Device table for advance */
53     ignored     = 0x0F00,       /* Was used in TrueType Open for MM fonts */
54     reserved    = 0xF000,       /* For future use */
55
56     devices     = 0x00F0        /* Mask for having any Device table */
57   };
58
59 /* All fields are options.  Only those available advance the value pointer. */
60 #if 0
61   SHORT         xPlacement;             /* Horizontal adjustment for
62                                          * placement--in design units */
63   SHORT         yPlacement;             /* Vertical adjustment for
64                                          * placement--in design units */
65   SHORT         xAdvance;               /* Horizontal adjustment for
66                                          * advance--in design units (only used
67                                          * for horizontal writing) */
68   SHORT         yAdvance;               /* Vertical adjustment for advance--in
69                                          * design units (only used for vertical
70                                          * writing) */
71   Offset        xPlaDevice;             /* Offset to Device table for
72                                          * horizontal placement--measured from
73                                          * beginning of PosTable (may be NULL) */
74   Offset        yPlaDevice;             /* Offset to Device table for vertical
75                                          * placement--measured from beginning
76                                          * of PosTable (may be NULL) */
77   Offset        xAdvDevice;             /* Offset to Device table for
78                                          * horizontal advance--measured from
79                                          * beginning of PosTable (may be NULL) */
80   Offset        yAdvDevice;             /* Offset to Device table for vertical
81                                          * advance--measured from beginning of
82                                          * PosTable (may be NULL) */
83 #endif
84
85   inline unsigned int get_len (void) const
86   { return _hb_popcount32 ((unsigned int) *this); }
87   inline unsigned int get_size (void) const
88   { return get_len () * Value::static_size; }
89
90   void apply_value (hb_ot_layout_context_t *layout,
91                     const void             *base,
92                     const Value            *values,
93                     hb_glyph_position_t    &glyph_pos) const
94   {
95     unsigned int x_ppem, y_ppem;
96     unsigned int format = *this;
97
98     if (!format) return;
99
100     /* design units -> fractional pixel */
101     if (format & xPlacement) glyph_pos.x_offset  += layout->scale_x (get_short (values++));
102     if (format & yPlacement) glyph_pos.y_offset  += layout->scale_y (get_short (values++));
103     if (format & xAdvance)   glyph_pos.x_advance += layout->scale_x (get_short (values++));
104     if (format & yAdvance)   glyph_pos.y_advance += layout->scale_y (get_short (values++));
105
106     if (!has_device ()) return;
107
108     x_ppem = layout->font->x_ppem;
109     y_ppem = layout->font->y_ppem;
110
111     if (!x_ppem && !y_ppem) return;
112
113     /* pixel -> fractional pixel */
114     if (format & xPlaDevice) {
115       if (x_ppem) glyph_pos.x_offset  += (base + get_device (values++)).get_x_delta (layout); else values++;
116     }
117     if (format & yPlaDevice) {
118       if (y_ppem) glyph_pos.y_offset  += (base + get_device (values++)).get_y_delta (layout); else values++;
119     }
120     if (format & xAdvDevice) {
121       if (x_ppem) glyph_pos.x_advance += (base + get_device (values++)).get_x_delta (layout); else values++;
122     }
123     if (format & yAdvDevice) {
124       if (y_ppem) glyph_pos.y_advance += (base + get_device (values++)).get_y_delta (layout); else values++;
125     }
126   }
127
128   private:
129   inline bool sanitize_value_devices (hb_sanitize_context_t *c, void *base, Value *values) {
130     unsigned int format = *this;
131
132     if (format & xPlacement) values++;
133     if (format & yPlacement) values++;
134     if (format & xAdvance)   values++;
135     if (format & yAdvance)   values++;
136
137     if ((format & xPlaDevice) && !get_device (values++).sanitize (c, base)) return false;
138     if ((format & yPlaDevice) && !get_device (values++).sanitize (c, base)) return false;
139     if ((format & xAdvDevice) && !get_device (values++).sanitize (c, base)) return false;
140     if ((format & yAdvDevice) && !get_device (values++).sanitize (c, base)) return false;
141
142     return true;
143   }
144
145   static inline OffsetTo<Device>& get_device (Value* value)
146   { return *CastP<OffsetTo<Device> > (value); }
147   static inline const OffsetTo<Device>& get_device (const Value* value)
148   { return *CastP<OffsetTo<Device> > (value); }
149
150   static inline const SHORT& get_short (const Value* value)
151   { return *CastP<SHORT> (value); }
152
153   public:
154
155   inline bool has_device (void) const {
156     unsigned int format = *this;
157     return (format & devices) != 0;
158   }
159
160   inline bool sanitize_value (hb_sanitize_context_t *c, void *base, Value *values) {
161     TRACE_SANITIZE ();
162     return c->check_range (values, get_size ())
163         && (!has_device () || sanitize_value_devices (c, base, values));
164   }
165
166   inline bool sanitize_values (hb_sanitize_context_t *c, void *base, Value *values, unsigned int count) {
167     TRACE_SANITIZE ();
168     unsigned int len = get_len ();
169
170     if (!c->check_array (values, get_size (), count)) return false;
171
172     if (!has_device ()) return true;
173
174     for (unsigned int i = 0; i < count; i++) {
175       if (!sanitize_value_devices (c, base, values))
176         return false;
177       values += len;
178     }
179
180     return true;
181   }
182
183   /* Just sanitize referenced Device tables.  Doesn't check the values themselves. */
184   inline bool sanitize_values_stride_unsafe (hb_sanitize_context_t *c, void *base, Value *values, unsigned int count, unsigned int stride) {
185     TRACE_SANITIZE ();
186
187     if (!has_device ()) return true;
188
189     for (unsigned int i = 0; i < count; i++) {
190       if (!sanitize_value_devices (c, base, values))
191         return false;
192       values += stride;
193     }
194
195     return true;
196   }
197 };
198
199
200 struct AnchorFormat1
201 {
202   friend struct Anchor;
203
204   private:
205   inline void get_anchor (hb_ot_layout_context_t *layout, hb_codepoint_t glyph_id HB_UNUSED,
206                           hb_position_t *x, hb_position_t *y) const
207   {
208       *x = layout->scale_x (xCoordinate);
209       *y = layout->scale_y (yCoordinate);
210   }
211
212   inline bool sanitize (hb_sanitize_context_t *c) {
213     TRACE_SANITIZE ();
214     return c->check_struct (this);
215   }
216
217   private:
218   USHORT        format;                 /* Format identifier--format = 1 */
219   SHORT         xCoordinate;            /* Horizontal value--in design units */
220   SHORT         yCoordinate;            /* Vertical value--in design units */
221   public:
222   DEFINE_SIZE_STATIC (6);
223 };
224
225 struct AnchorFormat2
226 {
227   friend struct Anchor;
228
229   private:
230   inline void get_anchor (hb_ot_layout_context_t *layout, hb_codepoint_t glyph_id,
231                           hb_position_t *x, hb_position_t *y) const
232   {
233       unsigned int x_ppem = layout->font->x_ppem;
234       unsigned int y_ppem = layout->font->y_ppem;
235       hb_position_t cx, cy;
236       hb_bool_t ret;
237
238       if (x_ppem || y_ppem)
239         ret = hb_font_get_contour_point (layout->font, layout->face, anchorPoint, glyph_id, &cx, &cy);
240       *x = x_ppem && ret ? cx : layout->scale_x (xCoordinate);
241       *y = y_ppem && ret ? cy : layout->scale_y (yCoordinate);
242   }
243
244   inline bool sanitize (hb_sanitize_context_t *c) {
245     TRACE_SANITIZE ();
246     return c->check_struct (this);
247   }
248
249   private:
250   USHORT        format;                 /* Format identifier--format = 2 */
251   SHORT         xCoordinate;            /* Horizontal value--in design units */
252   SHORT         yCoordinate;            /* Vertical value--in design units */
253   USHORT        anchorPoint;            /* Index to glyph contour point */
254   public:
255   DEFINE_SIZE_STATIC (8);
256 };
257
258 struct AnchorFormat3
259 {
260   friend struct Anchor;
261
262   private:
263   inline void get_anchor (hb_ot_layout_context_t *layout, hb_codepoint_t glyph_id HB_UNUSED,
264                           hb_position_t *x, hb_position_t *y) const
265   {
266       *x = layout->scale_x (xCoordinate);
267       *y = layout->scale_y (yCoordinate);
268
269       /* pixel -> fractional pixel */
270       if (layout->font->x_ppem)
271         *x += (this+xDeviceTable).get_x_delta (layout);
272       if (layout->font->y_ppem)
273         *y += (this+yDeviceTable).get_x_delta (layout);
274   }
275
276   inline bool sanitize (hb_sanitize_context_t *c) {
277     TRACE_SANITIZE ();
278     return c->check_struct (this)
279         && xDeviceTable.sanitize (c, this)
280         && yDeviceTable.sanitize (c, this);
281   }
282
283   private:
284   USHORT        format;                 /* Format identifier--format = 3 */
285   SHORT         xCoordinate;            /* Horizontal value--in design units */
286   SHORT         yCoordinate;            /* Vertical value--in design units */
287   OffsetTo<Device>
288                 xDeviceTable;           /* Offset to Device table for X
289                                          * coordinate-- from beginning of
290                                          * Anchor table (may be NULL) */
291   OffsetTo<Device>
292                 yDeviceTable;           /* Offset to Device table for Y
293                                          * coordinate-- from beginning of
294                                          * Anchor table (may be NULL) */
295   public:
296   DEFINE_SIZE_STATIC (10);
297 };
298
299 struct Anchor
300 {
301   inline void get_anchor (hb_ot_layout_context_t *layout, hb_codepoint_t glyph_id,
302                           hb_position_t *x, hb_position_t *y) const
303   {
304     *x = *y = 0;
305     switch (u.format) {
306     case 1: u.format1.get_anchor (layout, glyph_id, x, y); return;
307     case 2: u.format2.get_anchor (layout, glyph_id, x, y); return;
308     case 3: u.format3.get_anchor (layout, glyph_id, x, y); return;
309     default:                                                return;
310     }
311   }
312
313   inline bool sanitize (hb_sanitize_context_t *c) {
314     TRACE_SANITIZE ();
315     if (!u.format.sanitize (c)) return false;
316     switch (u.format) {
317     case 1: return u.format1.sanitize (c);
318     case 2: return u.format2.sanitize (c);
319     case 3: return u.format3.sanitize (c);
320     default:return true;
321     }
322   }
323
324   private:
325   union {
326   USHORT                format;         /* Format identifier */
327   AnchorFormat1         format1;
328   AnchorFormat2         format2;
329   AnchorFormat3         format3;
330   } u;
331   public:
332   DEFINE_SIZE_UNION (2, format);
333 };
334
335
336 struct AnchorMatrix
337 {
338   inline const Anchor& get_anchor (unsigned int row, unsigned int col, unsigned int cols) const {
339     if (unlikely (row >= rows || col >= cols)) return Null(Anchor);
340     return this+matrix[row * cols + col];
341   }
342
343   inline bool sanitize (hb_sanitize_context_t *c, unsigned int cols) {
344     TRACE_SANITIZE ();
345     if (!c->check_struct (this)) return false;
346     if (unlikely (rows > 0 && cols >= ((unsigned int) -1) / rows)) return false;
347     unsigned int count = rows * cols;
348     if (!c->check_array (matrix, matrix[0].static_size, count)) return false;
349     for (unsigned int i = 0; i < count; i++)
350       if (!matrix[i].sanitize (c, this)) return false;
351     return true;
352   }
353
354   USHORT        rows;                   /* Number of rows */
355   private:
356   OffsetTo<Anchor>
357                 matrix[VAR];            /* Matrix of offsets to Anchor tables--
358                                          * from beginning of AnchorMatrix table */
359   public:
360   DEFINE_SIZE_ARRAY (2, matrix);
361 };
362
363
364 struct MarkRecord
365 {
366   friend struct MarkArray;
367
368   inline bool sanitize (hb_sanitize_context_t *c, void *base) {
369     TRACE_SANITIZE ();
370     return c->check_struct (this)
371         && markAnchor.sanitize (c, base);
372   }
373
374   private:
375   USHORT        klass;                  /* Class defined for this mark */
376   OffsetTo<Anchor>
377                 markAnchor;             /* Offset to Anchor table--from
378                                          * beginning of MarkArray table */
379   public:
380   DEFINE_SIZE_STATIC (4);
381 };
382
383 struct MarkArray : ArrayOf<MarkRecord>  /* Array of MarkRecords--in Coverage order */
384 {
385   inline bool apply (hb_apply_context_t *c,
386                      unsigned int mark_index, unsigned int glyph_index,
387                      const AnchorMatrix &anchors, unsigned int class_count,
388                      unsigned int glyph_pos) const
389   {
390     TRACE_APPLY ();
391     const MarkRecord &record = ArrayOf<MarkRecord>::operator[](mark_index);
392     unsigned int mark_class = record.klass;
393
394     const Anchor& mark_anchor = this + record.markAnchor;
395     const Anchor& glyph_anchor = anchors.get_anchor (glyph_index, mark_class, class_count);
396
397     hb_position_t mark_x, mark_y, base_x, base_y;
398
399     mark_anchor.get_anchor (c->layout, c->buffer->info[c->buffer->i].codepoint, &mark_x, &mark_y);
400     glyph_anchor.get_anchor (c->layout, c->buffer->info[glyph_pos].codepoint, &base_x, &base_y);
401
402     hb_glyph_position_t &o = c->buffer->pos[c->buffer->i];
403     o.x_offset  = base_x - mark_x;
404     o.y_offset  = base_y - mark_y;
405     o.back()    = c->buffer->i - glyph_pos;
406
407     c->buffer->i++;
408     return true;
409   }
410
411   inline bool sanitize (hb_sanitize_context_t *c) {
412     TRACE_SANITIZE ();
413     return ArrayOf<MarkRecord>::sanitize (c, this);
414   }
415 };
416
417
418 /* Lookups */
419
420 struct SinglePosFormat1
421 {
422   friend struct SinglePos;
423
424   private:
425   inline bool apply (hb_apply_context_t *c) const
426   {
427     TRACE_APPLY ();
428     unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoint);
429     if (likely (index == NOT_COVERED))
430       return false;
431
432     valueFormat.apply_value (c->layout, this, values, c->buffer->pos[c->buffer->i]);
433
434     c->buffer->i++;
435     return true;
436   }
437
438   inline bool sanitize (hb_sanitize_context_t *c) {
439     TRACE_SANITIZE ();
440     return c->check_struct (this)
441         && coverage.sanitize (c, this)
442         && valueFormat.sanitize_value (c, this, values);
443   }
444
445   private:
446   USHORT        format;                 /* Format identifier--format = 1 */
447   OffsetTo<Coverage>
448                 coverage;               /* Offset to Coverage table--from
449                                          * beginning of subtable */
450   ValueFormat   valueFormat;            /* Defines the types of data in the
451                                          * ValueRecord */
452   ValueRecord   values;                 /* Defines positioning
453                                          * value(s)--applied to all glyphs in
454                                          * the Coverage table */
455   public:
456   DEFINE_SIZE_ARRAY (6, values);
457 };
458
459 struct SinglePosFormat2
460 {
461   friend struct SinglePos;
462
463   private:
464   inline bool apply (hb_apply_context_t *c) const
465   {
466     TRACE_APPLY ();
467     unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoint);
468     if (likely (index == NOT_COVERED))
469       return false;
470
471     if (likely (index >= valueCount))
472       return false;
473
474     valueFormat.apply_value (c->layout, this,
475                              &values[index * valueFormat.get_len ()],
476                              c->buffer->pos[c->buffer->i]);
477
478     c->buffer->i++;
479     return true;
480   }
481
482   inline bool sanitize (hb_sanitize_context_t *c) {
483     TRACE_SANITIZE ();
484     return c->check_struct (this)
485         && coverage.sanitize (c, this)
486         && valueFormat.sanitize_values (c, this, values, valueCount);
487   }
488
489   private:
490   USHORT        format;                 /* Format identifier--format = 2 */
491   OffsetTo<Coverage>
492                 coverage;               /* Offset to Coverage table--from
493                                          * beginning of subtable */
494   ValueFormat   valueFormat;            /* Defines the types of data in the
495                                          * ValueRecord */
496   USHORT        valueCount;             /* Number of ValueRecords */
497   ValueRecord   values;                 /* Array of ValueRecords--positioning
498                                          * values applied to glyphs */
499   public:
500   DEFINE_SIZE_ARRAY (8, values);
501 };
502
503 struct SinglePos
504 {
505   friend struct PosLookupSubTable;
506
507   private:
508   inline bool apply (hb_apply_context_t *c) const
509   {
510     TRACE_APPLY ();
511     switch (u.format) {
512     case 1: return u.format1.apply (c);
513     case 2: return u.format2.apply (c);
514     default:return false;
515     }
516   }
517
518   inline bool sanitize (hb_sanitize_context_t *c) {
519     TRACE_SANITIZE ();
520     if (!u.format.sanitize (c)) return false;
521     switch (u.format) {
522     case 1: return u.format1.sanitize (c);
523     case 2: return u.format2.sanitize (c);
524     default:return true;
525     }
526   }
527
528   private:
529   union {
530   USHORT                format;         /* Format identifier */
531   SinglePosFormat1      format1;
532   SinglePosFormat2      format2;
533   } u;
534 };
535
536
537 struct PairValueRecord
538 {
539   friend struct PairSet;
540
541   private:
542   GlyphID       secondGlyph;            /* GlyphID of second glyph in the
543                                          * pair--first glyph is listed in the
544                                          * Coverage table */
545   ValueRecord   values;                 /* Positioning data for the first glyph
546                                          * followed by for second glyph */
547   public:
548   DEFINE_SIZE_ARRAY (2, values);
549 };
550
551 struct PairSet
552 {
553   friend struct PairPosFormat1;
554
555   inline bool apply (hb_apply_context_t *c,
556                      const ValueFormat *valueFormats,
557                      unsigned int pos) const
558   {
559     TRACE_APPLY ();
560     unsigned int len1 = valueFormats[0].get_len ();
561     unsigned int len2 = valueFormats[1].get_len ();
562     unsigned int record_size = USHORT::static_size * (1 + len1 + len2);
563
564     unsigned int count = len;
565     const PairValueRecord *record = CastP<PairValueRecord> (array);
566     for (unsigned int i = 0; i < count; i++)
567     {
568       if (c->buffer->info[pos].codepoint == record->secondGlyph)
569       {
570         valueFormats[0].apply_value (c->layout, this, &record->values[0], c->buffer->pos[c->buffer->i]);
571         valueFormats[1].apply_value (c->layout, this, &record->values[len1], c->buffer->pos[pos]);
572         if (len2)
573           pos++;
574         c->buffer->i = pos;
575         return true;
576       }
577       record = &StructAtOffset<PairValueRecord> (record, record_size);
578     }
579
580     return false;
581   }
582
583   struct sanitize_closure_t {
584     void *base;
585     ValueFormat *valueFormats;
586     unsigned int len1; /* valueFormats[0].get_len() */
587     unsigned int stride; /* 1 + len1 + len2 */
588   };
589
590   inline bool sanitize (hb_sanitize_context_t *c, const sanitize_closure_t *closure) {
591     TRACE_SANITIZE ();
592     if (!(c->check_struct (this)
593        && c->check_array (array, USHORT::static_size * closure->stride, len))) return false;
594
595     unsigned int count = len;
596     PairValueRecord *record = CastP<PairValueRecord> (array);
597     return closure->valueFormats[0].sanitize_values_stride_unsafe (c, closure->base, &record->values[0], count, closure->stride)
598         && closure->valueFormats[1].sanitize_values_stride_unsafe (c, closure->base, &record->values[closure->len1], count, closure->stride);
599   }
600
601   private:
602   USHORT        len;                    /* Number of PairValueRecords */
603   USHORT        array[VAR];             /* Array of PairValueRecords--ordered
604                                          * by GlyphID of the second glyph */
605   public:
606   DEFINE_SIZE_ARRAY (2, array);
607 };
608
609 struct PairPosFormat1
610 {
611   friend struct PairPos;
612
613   private:
614   inline bool apply (hb_apply_context_t *c) const
615   {
616     TRACE_APPLY ();
617     unsigned int end = MIN (c->buffer->len, c->buffer->i + c->context_length);
618     if (unlikely (c->buffer->i + 2 > end))
619       return false;
620
621     unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoint);
622     if (likely (index == NOT_COVERED))
623       return false;
624
625     unsigned int j = c->buffer->i + 1;
626     while (_hb_ot_layout_skip_mark (c->layout->face, &c->buffer->info[j], c->lookup_flag, NULL))
627     {
628       if (unlikely (j == end))
629         return false;
630       j++;
631     }
632
633     return (this+pairSet[index]).apply (c, &valueFormat1, j);
634   }
635
636   inline bool sanitize (hb_sanitize_context_t *c) {
637     TRACE_SANITIZE ();
638
639     unsigned int len1 = valueFormat1.get_len ();
640     unsigned int len2 = valueFormat2.get_len ();
641     PairSet::sanitize_closure_t closure = {
642       this,
643       &valueFormat1,
644       len1,
645       1 + len1 + len2
646     };
647
648     return c->check_struct (this)
649         && coverage.sanitize (c, this)
650         && pairSet.sanitize (c, this, &closure);
651   }
652
653   private:
654   USHORT        format;                 /* Format identifier--format = 1 */
655   OffsetTo<Coverage>
656                 coverage;               /* Offset to Coverage table--from
657                                          * beginning of subtable */
658   ValueFormat   valueFormat1;           /* Defines the types of data in
659                                          * ValueRecord1--for the first glyph
660                                          * in the pair--may be zero (0) */
661   ValueFormat   valueFormat2;           /* Defines the types of data in
662                                          * ValueRecord2--for the second glyph
663                                          * in the pair--may be zero (0) */
664   OffsetArrayOf<PairSet>
665                 pairSet;                /* Array of PairSet tables
666                                          * ordered by Coverage Index */
667   public:
668   DEFINE_SIZE_ARRAY (10, pairSet);
669 };
670
671 struct PairPosFormat2
672 {
673   friend struct PairPos;
674
675   private:
676   inline bool apply (hb_apply_context_t *c) const
677   {
678     TRACE_APPLY ();
679     unsigned int end = MIN (c->buffer->len, c->buffer->i + c->context_length);
680     if (unlikely (c->buffer->i + 2 > end))
681       return false;
682
683     unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoint);
684     if (likely (index == NOT_COVERED))
685       return false;
686
687     unsigned int j = c->buffer->i + 1;
688     while (_hb_ot_layout_skip_mark (c->layout->face, &c->buffer->info[j], c->lookup_flag, NULL))
689     {
690       if (unlikely (j == end))
691         return false;
692       j++;
693     }
694
695     unsigned int len1 = valueFormat1.get_len ();
696     unsigned int len2 = valueFormat2.get_len ();
697     unsigned int record_len = len1 + len2;
698
699     unsigned int klass1 = (this+classDef1) (c->buffer->info[c->buffer->i].codepoint);
700     unsigned int klass2 = (this+classDef2) (c->buffer->info[j].codepoint);
701     if (unlikely (klass1 >= class1Count || klass2 >= class2Count))
702       return false;
703
704     const Value *v = &values[record_len * (klass1 * class2Count + klass2)];
705     valueFormat1.apply_value (c->layout, this, v, c->buffer->pos[c->buffer->i]);
706     valueFormat2.apply_value (c->layout, this, v + len1, c->buffer->pos[j]);
707
708     if (len2)
709       j++;
710     c->buffer->i = j;
711
712     return true;
713   }
714
715   inline bool sanitize (hb_sanitize_context_t *c) {
716     TRACE_SANITIZE ();
717     if (!(c->check_struct (this)
718        && coverage.sanitize (c, this)
719        && classDef1.sanitize (c, this)
720        && classDef2.sanitize (c, this))) return false;
721
722     unsigned int len1 = valueFormat1.get_len ();
723     unsigned int len2 = valueFormat2.get_len ();
724     unsigned int stride = len1 + len2;
725     unsigned int record_size = valueFormat1.get_size () + valueFormat2.get_size ();
726     unsigned int count = (unsigned int) class1Count * (unsigned int) class2Count;
727     return c->check_array (values, record_size, count) &&
728            valueFormat1.sanitize_values_stride_unsafe (c, this, &values[0], count, stride) &&
729            valueFormat2.sanitize_values_stride_unsafe (c, this, &values[len1], count, stride);
730   }
731
732   private:
733   USHORT        format;                 /* Format identifier--format = 2 */
734   OffsetTo<Coverage>
735                 coverage;               /* Offset to Coverage table--from
736                                          * beginning of subtable */
737   ValueFormat   valueFormat1;           /* ValueRecord definition--for the
738                                          * first glyph of the pair--may be zero
739                                          * (0) */
740   ValueFormat   valueFormat2;           /* ValueRecord definition--for the
741                                          * second glyph of the pair--may be
742                                          * zero (0) */
743   OffsetTo<ClassDef>
744                 classDef1;              /* Offset to ClassDef table--from
745                                          * beginning of PairPos subtable--for
746                                          * the first glyph of the pair */
747   OffsetTo<ClassDef>
748                 classDef2;              /* Offset to ClassDef table--from
749                                          * beginning of PairPos subtable--for
750                                          * the second glyph of the pair */
751   USHORT        class1Count;            /* Number of classes in ClassDef1
752                                          * table--includes Class0 */
753   USHORT        class2Count;            /* Number of classes in ClassDef2
754                                          * table--includes Class0 */
755   ValueRecord   values;                 /* Matrix of value pairs:
756                                          * class1-major, class2-minor,
757                                          * Each entry has value1 and value2 */
758   public:
759   DEFINE_SIZE_ARRAY (16, values);
760 };
761
762 struct PairPos
763 {
764   friend struct PosLookupSubTable;
765
766   private:
767   inline bool apply (hb_apply_context_t *c) const
768   {
769     TRACE_APPLY ();
770     switch (u.format) {
771     case 1: return u.format1.apply (c);
772     case 2: return u.format2.apply (c);
773     default:return false;
774     }
775   }
776
777   inline bool sanitize (hb_sanitize_context_t *c) {
778     TRACE_SANITIZE ();
779     if (!u.format.sanitize (c)) return false;
780     switch (u.format) {
781     case 1: return u.format1.sanitize (c);
782     case 2: return u.format2.sanitize (c);
783     default:return true;
784     }
785   }
786
787   private:
788   union {
789   USHORT                format;         /* Format identifier */
790   PairPosFormat1        format1;
791   PairPosFormat2        format2;
792   } u;
793 };
794
795
796 struct EntryExitRecord
797 {
798   friend struct CursivePosFormat1;
799
800   inline bool sanitize (hb_sanitize_context_t *c, void *base) {
801     TRACE_SANITIZE ();
802     return entryAnchor.sanitize (c, base)
803         && exitAnchor.sanitize (c, base);
804   }
805
806   private:
807   OffsetTo<Anchor>
808                 entryAnchor;            /* Offset to EntryAnchor table--from
809                                          * beginning of CursivePos
810                                          * subtable--may be NULL */
811   OffsetTo<Anchor>
812                 exitAnchor;             /* Offset to ExitAnchor table--from
813                                          * beginning of CursivePos
814                                          * subtable--may be NULL */
815   public:
816   DEFINE_SIZE_STATIC (4);
817 };
818
819 struct CursivePosFormat1
820 {
821   friend struct CursivePos;
822
823   private:
824   inline bool apply (hb_apply_context_t *c) const
825   {
826     TRACE_APPLY ();
827
828     /* We don't handle mark glyphs here. */
829     if (c->property == HB_OT_LAYOUT_GLYPH_CLASS_MARK)
830       return false;
831
832     unsigned int end = MIN (c->buffer->len, c->buffer->i + c->context_length);
833     if (unlikely (c->buffer->i + 2 > end))
834       return false;
835
836     const EntryExitRecord &this_record = entryExitRecord[(this+coverage) (c->buffer->info[c->buffer->i].codepoint)];
837     if (!this_record.exitAnchor)
838       return false;
839
840     unsigned int j = c->buffer->i + 1;
841     while (_hb_ot_layout_skip_mark (c->layout->face, &c->buffer->info[j], c->lookup_flag, NULL))
842     {
843       if (unlikely (j == end))
844         return false;
845       j++;
846     }
847
848     const EntryExitRecord &next_record = entryExitRecord[(this+coverage) (c->buffer->info[j].codepoint)];
849     if (!next_record.entryAnchor)
850       return false;
851
852     unsigned int i = c->buffer->i;
853
854     hb_position_t entry_x, entry_y, exit_x, exit_y;
855     (this+this_record.exitAnchor).get_anchor (c->layout, c->buffer->info[i].codepoint, &exit_x, &exit_y);
856     (this+next_record.entryAnchor).get_anchor (c->layout, c->buffer->info[j].codepoint, &entry_x, &entry_y);
857
858     hb_direction_t direction = c->buffer->props.direction;
859
860     /* Align the exit anchor of the left/top glyph with the entry anchor of the right/bottom glyph
861      * by adjusting advance of the left/top glyph. */
862     if (HB_DIRECTION_IS_BACKWARD (direction))
863     {
864       if (likely (HB_DIRECTION_IS_HORIZONTAL (direction)))
865         c->buffer->pos[j].x_advance = c->buffer->pos[j].x_offset + entry_x - exit_x;
866       else
867         c->buffer->pos[j].y_advance = c->buffer->pos[j].y_offset + entry_y - exit_y;
868     }
869     else
870     {
871       if (likely (HB_DIRECTION_IS_HORIZONTAL (direction)))
872         c->buffer->pos[i].x_advance = c->buffer->pos[i].x_offset + exit_x - entry_x;
873       else
874         c->buffer->pos[i].y_advance = c->buffer->pos[i].y_offset + exit_y - entry_y;
875     }
876
877     if  (c->lookup_flag & LookupFlag::RightToLeft)
878     {
879       c->buffer->pos[i].cursive_chain() = j - i;
880       if (likely (HB_DIRECTION_IS_HORIZONTAL (direction)))
881         c->buffer->pos[i].y_offset = entry_y - exit_y;
882       else
883         c->buffer->pos[i].x_offset = entry_x - exit_x;
884     }
885     else
886     {
887       c->buffer->pos[j].cursive_chain() = i - j;
888       if (likely (HB_DIRECTION_IS_HORIZONTAL (direction)))
889         c->buffer->pos[j].y_offset = exit_y - entry_y;
890       else
891         c->buffer->pos[j].x_offset = exit_x - entry_x;
892     }
893
894     c->buffer->i = j;
895     return true;
896   }
897
898   inline bool sanitize (hb_sanitize_context_t *c) {
899     TRACE_SANITIZE ();
900     return coverage.sanitize (c, this)
901         && entryExitRecord.sanitize (c, this);
902   }
903
904   private:
905   USHORT        format;                 /* Format identifier--format = 1 */
906   OffsetTo<Coverage>
907                 coverage;               /* Offset to Coverage table--from
908                                          * beginning of subtable */
909   ArrayOf<EntryExitRecord>
910                 entryExitRecord;        /* Array of EntryExit records--in
911                                          * Coverage Index order */
912   public:
913   DEFINE_SIZE_ARRAY (6, entryExitRecord);
914 };
915
916 struct CursivePos
917 {
918   friend struct PosLookupSubTable;
919
920   private:
921   inline bool apply (hb_apply_context_t *c) const
922   {
923     TRACE_APPLY ();
924     switch (u.format) {
925     case 1: return u.format1.apply (c);
926     default:return false;
927     }
928   }
929
930   inline bool sanitize (hb_sanitize_context_t *c) {
931     TRACE_SANITIZE ();
932     if (!u.format.sanitize (c)) return false;
933     switch (u.format) {
934     case 1: return u.format1.sanitize (c);
935     default:return true;
936     }
937   }
938
939   private:
940   union {
941   USHORT                format;         /* Format identifier */
942   CursivePosFormat1     format1;
943   } u;
944 };
945
946
947 typedef AnchorMatrix BaseArray;         /* base-major--
948                                          * in order of BaseCoverage Index--,
949                                          * mark-minor--
950                                          * ordered by class--zero-based. */
951
952 struct MarkBasePosFormat1
953 {
954   friend struct MarkBasePos;
955
956   private:
957   inline bool apply (hb_apply_context_t *c) const
958   {
959     TRACE_APPLY ();
960     unsigned int mark_index = (this+markCoverage) (c->buffer->info[c->buffer->i].codepoint);
961     if (likely (mark_index == NOT_COVERED))
962       return false;
963
964     /* now we search backwards for a non-mark glyph */
965     unsigned int property;
966     unsigned int j = c->buffer->i;
967     do
968     {
969       if (unlikely (!j))
970         return false;
971       j--;
972     } while (_hb_ot_layout_skip_mark (c->layout->face, &c->buffer->info[j], LookupFlag::IgnoreMarks, &property));
973
974     /* The following assertion is too strong, so we've disabled it. */
975     if (false && !(property & HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH))
976       return false;
977
978     unsigned int base_index = (this+baseCoverage) (c->buffer->info[j].codepoint);
979     if (base_index == NOT_COVERED)
980       return false;
981
982     return (this+markArray).apply (c, mark_index, base_index, this+baseArray, classCount, j);
983   }
984
985   inline bool sanitize (hb_sanitize_context_t *c) {
986     TRACE_SANITIZE ();
987     return c->check_struct (this)
988         && markCoverage.sanitize (c, this)
989         && baseCoverage.sanitize (c, this)
990         && markArray.sanitize (c, this)
991         && baseArray.sanitize (c, this, (unsigned int) classCount);
992   }
993
994   private:
995   USHORT        format;                 /* Format identifier--format = 1 */
996   OffsetTo<Coverage>
997                 markCoverage;           /* Offset to MarkCoverage table--from
998                                          * beginning of MarkBasePos subtable */
999   OffsetTo<Coverage>
1000                 baseCoverage;           /* Offset to BaseCoverage table--from
1001                                          * beginning of MarkBasePos subtable */
1002   USHORT        classCount;             /* Number of classes defined for marks */
1003   OffsetTo<MarkArray>
1004                 markArray;              /* Offset to MarkArray table--from
1005                                          * beginning of MarkBasePos subtable */
1006   OffsetTo<BaseArray>
1007                 baseArray;              /* Offset to BaseArray table--from
1008                                          * beginning of MarkBasePos subtable */
1009   public:
1010   DEFINE_SIZE_STATIC (12);
1011 };
1012
1013 struct MarkBasePos
1014 {
1015   friend struct PosLookupSubTable;
1016
1017   private:
1018   inline bool apply (hb_apply_context_t *c) const
1019   {
1020     TRACE_APPLY ();
1021     switch (u.format) {
1022     case 1: return u.format1.apply (c);
1023     default:return false;
1024     }
1025   }
1026
1027   inline bool sanitize (hb_sanitize_context_t *c) {
1028     TRACE_SANITIZE ();
1029     if (!u.format.sanitize (c)) return false;
1030     switch (u.format) {
1031     case 1: return u.format1.sanitize (c);
1032     default:return true;
1033     }
1034   }
1035
1036   private:
1037   union {
1038   USHORT                format;         /* Format identifier */
1039   MarkBasePosFormat1    format1;
1040   } u;
1041 };
1042
1043
1044 typedef AnchorMatrix LigatureAttach;    /* component-major--
1045                                          * in order of writing direction--,
1046                                          * mark-minor--
1047                                          * ordered by class--zero-based. */
1048
1049 typedef OffsetListOf<LigatureAttach> LigatureArray;
1050                                         /* Array of LigatureAttach
1051                                          * tables ordered by
1052                                          * LigatureCoverage Index */
1053
1054 struct MarkLigPosFormat1
1055 {
1056   friend struct MarkLigPos;
1057
1058   private:
1059   inline bool apply (hb_apply_context_t *c) const
1060   {
1061     TRACE_APPLY ();
1062     unsigned int mark_index = (this+markCoverage) (c->buffer->info[c->buffer->i].codepoint);
1063     if (likely (mark_index == NOT_COVERED))
1064       return false;
1065
1066     /* now we search backwards for a non-mark glyph */
1067     unsigned int property;
1068     unsigned int j = c->buffer->i;
1069     do
1070     {
1071       if (unlikely (!j))
1072         return false;
1073       j--;
1074     } while (_hb_ot_layout_skip_mark (c->layout->face, &c->buffer->info[j], LookupFlag::IgnoreMarks, &property));
1075
1076     /* The following assertion is too strong, so we've disabled it. */
1077     if (false && !(property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE))
1078       return false;
1079
1080     unsigned int lig_index = (this+ligatureCoverage) (c->buffer->info[j].codepoint);
1081     if (lig_index == NOT_COVERED)
1082       return false;
1083
1084     const LigatureArray& lig_array = this+ligatureArray;
1085     const LigatureAttach& lig_attach = lig_array[lig_index];
1086
1087     /* Find component to attach to */
1088     unsigned int comp_count = lig_attach.rows;
1089     if (unlikely (!comp_count))
1090       return false;
1091     unsigned int comp_index;
1092     /* We must now check whether the ligature ID of the current mark glyph
1093      * is identical to the ligature ID of the found ligature.  If yes, we
1094      * can directly use the component index.  If not, we attach the mark
1095      * glyph to the last component of the ligature. */
1096     if (c->buffer->info[j].lig_id() && c->buffer->info[j].lig_id() == c->buffer->info[c->buffer->i].lig_id() && c->buffer->info[c->buffer->i].component())
1097     {
1098       comp_index = c->buffer->info[c->buffer->i].component() - 1;
1099       if (comp_index >= comp_count)
1100         comp_index = comp_count - 1;
1101     }
1102     else
1103       comp_index = comp_count - 1;
1104
1105     return (this+markArray).apply (c, mark_index, comp_index, lig_attach, classCount, j);
1106   }
1107
1108   inline bool sanitize (hb_sanitize_context_t *c) {
1109     TRACE_SANITIZE ();
1110     return c->check_struct (this)
1111         && markCoverage.sanitize (c, this)
1112         && ligatureCoverage.sanitize (c, this)
1113         && markArray.sanitize (c, this)
1114         && ligatureArray.sanitize (c, this, (unsigned int) classCount);
1115   }
1116
1117   private:
1118   USHORT        format;                 /* Format identifier--format = 1 */
1119   OffsetTo<Coverage>
1120                 markCoverage;           /* Offset to Mark Coverage table--from
1121                                          * beginning of MarkLigPos subtable */
1122   OffsetTo<Coverage>
1123                 ligatureCoverage;       /* Offset to Ligature Coverage
1124                                          * table--from beginning of MarkLigPos
1125                                          * subtable */
1126   USHORT        classCount;             /* Number of defined mark classes */
1127   OffsetTo<MarkArray>
1128                 markArray;              /* Offset to MarkArray table--from
1129                                          * beginning of MarkLigPos subtable */
1130   OffsetTo<LigatureArray>
1131                 ligatureArray;          /* Offset to LigatureArray table--from
1132                                          * beginning of MarkLigPos subtable */
1133   public:
1134   DEFINE_SIZE_STATIC (12);
1135 };
1136
1137 struct MarkLigPos
1138 {
1139   friend struct PosLookupSubTable;
1140
1141   private:
1142   inline bool apply (hb_apply_context_t *c) const
1143   {
1144     TRACE_APPLY ();
1145     switch (u.format) {
1146     case 1: return u.format1.apply (c);
1147     default:return false;
1148     }
1149   }
1150
1151   inline bool sanitize (hb_sanitize_context_t *c) {
1152     TRACE_SANITIZE ();
1153     if (!u.format.sanitize (c)) return false;
1154     switch (u.format) {
1155     case 1: return u.format1.sanitize (c);
1156     default:return true;
1157     }
1158   }
1159
1160   private:
1161   union {
1162   USHORT                format;         /* Format identifier */
1163   MarkLigPosFormat1     format1;
1164   } u;
1165 };
1166
1167
1168 typedef AnchorMatrix Mark2Array;        /* mark2-major--
1169                                          * in order of Mark2Coverage Index--,
1170                                          * mark1-minor--
1171                                          * ordered by class--zero-based. */
1172
1173 struct MarkMarkPosFormat1
1174 {
1175   friend struct MarkMarkPos;
1176
1177   private:
1178   inline bool apply (hb_apply_context_t *c) const
1179   {
1180     TRACE_APPLY ();
1181     unsigned int mark1_index = (this+mark1Coverage) (c->buffer->info[c->buffer->i].codepoint);
1182     if (likely (mark1_index == NOT_COVERED))
1183       return false;
1184
1185     /* now we search backwards for a suitable mark glyph until a non-mark glyph */
1186     unsigned int property;
1187     unsigned int j = c->buffer->i;
1188     do
1189     {
1190       if (unlikely (!j))
1191         return false;
1192       j--;
1193     } while (_hb_ot_layout_skip_mark (c->layout->face, &c->buffer->info[j], c->lookup_flag, &property));
1194
1195     if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK))
1196       return false;
1197
1198     /* Two marks match only if they belong to the same base, or same component
1199      * of the same ligature.  That is, the component numbers must match, and
1200      * if those are non-zero, the ligid number should also match. */
1201     if ((c->buffer->info[j].component() != c->buffer->info[c->buffer->i].component()) ||
1202         (c->buffer->info[j].component() && c->buffer->info[j].lig_id() != c->buffer->info[c->buffer->i].lig_id()))
1203       return false;
1204
1205     unsigned int mark2_index = (this+mark2Coverage) (c->buffer->info[j].codepoint);
1206     if (mark2_index == NOT_COVERED)
1207       return false;
1208
1209     return (this+mark1Array).apply (c, mark1_index, mark2_index, this+mark2Array, classCount, j);
1210   }
1211
1212   inline bool sanitize (hb_sanitize_context_t *c) {
1213     TRACE_SANITIZE ();
1214     return c->check_struct (this)
1215         && mark1Coverage.sanitize (c, this)
1216         && mark2Coverage.sanitize (c, this)
1217         && mark1Array.sanitize (c, this)
1218         && mark2Array.sanitize (c, this, (unsigned int) classCount);
1219   }
1220
1221   private:
1222   USHORT        format;                 /* Format identifier--format = 1 */
1223   OffsetTo<Coverage>
1224                 mark1Coverage;          /* Offset to Combining Mark1 Coverage
1225                                          * table--from beginning of MarkMarkPos
1226                                          * subtable */
1227   OffsetTo<Coverage>
1228                 mark2Coverage;          /* Offset to Combining Mark2 Coverage
1229                                          * table--from beginning of MarkMarkPos
1230                                          * subtable */
1231   USHORT        classCount;             /* Number of defined mark classes */
1232   OffsetTo<MarkArray>
1233                 mark1Array;             /* Offset to Mark1Array table--from
1234                                          * beginning of MarkMarkPos subtable */
1235   OffsetTo<Mark2Array>
1236                 mark2Array;             /* Offset to Mark2Array table--from
1237                                          * beginning of MarkMarkPos subtable */
1238   public:
1239   DEFINE_SIZE_STATIC (12);
1240 };
1241
1242 struct MarkMarkPos
1243 {
1244   friend struct PosLookupSubTable;
1245
1246   private:
1247   inline bool apply (hb_apply_context_t *c) const
1248   {
1249     TRACE_APPLY ();
1250     switch (u.format) {
1251     case 1: return u.format1.apply (c);
1252     default:return false;
1253     }
1254   }
1255
1256   inline bool sanitize (hb_sanitize_context_t *c) {
1257     TRACE_SANITIZE ();
1258     if (!u.format.sanitize (c)) return false;
1259     switch (u.format) {
1260     case 1: return u.format1.sanitize (c);
1261     default:return true;
1262     }
1263   }
1264
1265   private:
1266   union {
1267   USHORT                format;         /* Format identifier */
1268   MarkMarkPosFormat1    format1;
1269   } u;
1270 };
1271
1272
1273 HB_BEGIN_DECLS
1274 static inline bool position_lookup (hb_apply_context_t *c, unsigned int lookup_index);
1275 HB_END_DECLS
1276
1277 struct ContextPos : Context
1278 {
1279   friend struct PosLookupSubTable;
1280
1281   private:
1282   inline bool apply (hb_apply_context_t *c) const
1283   {
1284     TRACE_APPLY ();
1285     return Context::apply (c, position_lookup);
1286   }
1287 };
1288
1289 struct ChainContextPos : ChainContext
1290 {
1291   friend struct PosLookupSubTable;
1292
1293   private:
1294   inline bool apply (hb_apply_context_t *c) const
1295   {
1296     TRACE_APPLY ();
1297     return ChainContext::apply (c, position_lookup);
1298   }
1299 };
1300
1301
1302 struct ExtensionPos : Extension
1303 {
1304   friend struct PosLookupSubTable;
1305
1306   private:
1307   inline const struct PosLookupSubTable& get_subtable (void) const
1308   {
1309     unsigned int offset = get_offset ();
1310     if (unlikely (!offset)) return Null(PosLookupSubTable);
1311     return StructAtOffset<PosLookupSubTable> (this, offset);
1312   }
1313
1314   inline bool apply (hb_apply_context_t *c) const;
1315
1316   inline bool sanitize (hb_sanitize_context_t *c);
1317 };
1318
1319
1320
1321 /*
1322  * PosLookup
1323  */
1324
1325
1326 struct PosLookupSubTable
1327 {
1328   friend struct PosLookup;
1329
1330   enum {
1331     Single              = 1,
1332     Pair                = 2,
1333     Cursive             = 3,
1334     MarkBase            = 4,
1335     MarkLig             = 5,
1336     MarkMark            = 6,
1337     Context             = 7,
1338     ChainContext        = 8,
1339     Extension           = 9
1340   };
1341
1342   inline bool apply (hb_apply_context_t *c, unsigned int lookup_type) const
1343   {
1344     TRACE_APPLY ();
1345     switch (lookup_type) {
1346     case Single:                return u.single.apply (c);
1347     case Pair:                  return u.pair.apply (c);
1348     case Cursive:               return u.cursive.apply (c);
1349     case MarkBase:              return u.markBase.apply (c);
1350     case MarkLig:               return u.markLig.apply (c);
1351     case MarkMark:              return u.markMark.apply (c);
1352     case Context:               return u.c.apply (c);
1353     case ChainContext:          return u.chainContext.apply (c);
1354     case Extension:             return u.extension.apply (c);
1355     default:return false;
1356     }
1357   }
1358
1359   inline bool sanitize (hb_sanitize_context_t *c, unsigned int lookup_type) {
1360     TRACE_SANITIZE ();
1361     switch (lookup_type) {
1362     case Single:                return u.single.sanitize (c);
1363     case Pair:                  return u.pair.sanitize (c);
1364     case Cursive:               return u.cursive.sanitize (c);
1365     case MarkBase:              return u.markBase.sanitize (c);
1366     case MarkLig:               return u.markLig.sanitize (c);
1367     case MarkMark:              return u.markMark.sanitize (c);
1368     case Context:               return u.c.sanitize (c);
1369     case ChainContext:          return u.chainContext.sanitize (c);
1370     case Extension:             return u.extension.sanitize (c);
1371     default:return true;
1372     }
1373   }
1374
1375   private:
1376   union {
1377   USHORT                sub_format;
1378   SinglePos             single;
1379   PairPos               pair;
1380   CursivePos            cursive;
1381   MarkBasePos           markBase;
1382   MarkLigPos            markLig;
1383   MarkMarkPos           markMark;
1384   ContextPos            c;
1385   ChainContextPos       chainContext;
1386   ExtensionPos          extension;
1387   } u;
1388   public:
1389   DEFINE_SIZE_UNION (2, sub_format);
1390 };
1391
1392
1393 struct PosLookup : Lookup
1394 {
1395   inline const PosLookupSubTable& get_subtable (unsigned int i) const
1396   { return this+CastR<OffsetArrayOf<PosLookupSubTable> > (subTable)[i]; }
1397
1398   inline bool apply_once (hb_ot_layout_context_t *layout,
1399                           hb_buffer_t *buffer,
1400                           hb_mask_t lookup_mask,
1401                           unsigned int context_length,
1402                           unsigned int nesting_level_left) const
1403   {
1404     unsigned int lookup_type = get_type ();
1405     hb_apply_context_t c[1] = {{0}};
1406
1407     c->layout = layout;
1408     c->buffer = buffer;
1409     c->lookup_mask = lookup_mask;
1410     c->context_length = context_length;
1411     c->nesting_level_left = nesting_level_left;
1412     c->lookup_flag = get_flag ();
1413
1414     if (!_hb_ot_layout_check_glyph_property (c->layout->face, &c->buffer->info[c->buffer->i], c->lookup_flag, &c->property))
1415       return false;
1416
1417     for (unsigned int i = 0; i < get_subtable_count (); i++)
1418       if (get_subtable (i).apply (c, lookup_type))
1419         return true;
1420
1421     return false;
1422   }
1423
1424    inline bool apply_string (hb_ot_layout_context_t *layout,
1425                              hb_buffer_t *buffer,
1426                              hb_mask_t    mask) const
1427   {
1428     bool ret = false;
1429
1430     if (unlikely (!buffer->len))
1431       return false;
1432
1433     buffer->i = 0;
1434     while (buffer->i < buffer->len)
1435     {
1436       if ((buffer->info[buffer->i].mask & mask) &&
1437           apply_once (layout, buffer, mask, NO_CONTEXT, MAX_NESTING_LEVEL))
1438         ret = true;
1439       else
1440         buffer->i++;
1441     }
1442
1443     return ret;
1444   }
1445
1446   inline bool sanitize (hb_sanitize_context_t *c) {
1447     TRACE_SANITIZE ();
1448     if (unlikely (!Lookup::sanitize (c))) return false;
1449     OffsetArrayOf<PosLookupSubTable> &list = CastR<OffsetArrayOf<PosLookupSubTable> > (subTable);
1450     return list.sanitize (c, this, get_type ());
1451   }
1452 };
1453
1454 typedef OffsetListOf<PosLookup> PosLookupList;
1455
1456 /*
1457  * GPOS
1458  */
1459
1460 struct GPOS : GSUBGPOS
1461 {
1462   static const hb_tag_t Tag     = HB_OT_TAG_GPOS;
1463
1464   inline const PosLookup& get_lookup (unsigned int i) const
1465   { return CastR<PosLookup> (GSUBGPOS::get_lookup (i)); }
1466
1467   inline bool position_lookup (hb_ot_layout_context_t *layout,
1468                                hb_buffer_t  *buffer,
1469                                unsigned int  lookup_index,
1470                                hb_mask_t     mask) const
1471   { return get_lookup (lookup_index).apply_string (layout, buffer, mask); }
1472
1473   inline bool sanitize (hb_sanitize_context_t *c) {
1474     TRACE_SANITIZE ();
1475     if (unlikely (!GSUBGPOS::sanitize (c))) return false;
1476     OffsetTo<PosLookupList> &list = CastR<OffsetTo<PosLookupList> > (lookupList);
1477     return list.sanitize (c, this);
1478   }
1479   public:
1480   DEFINE_SIZE_STATIC (10);
1481 };
1482
1483
1484 /* Out-of-class implementation for methods recursing */
1485
1486 inline bool ExtensionPos::apply (hb_apply_context_t *c) const
1487 {
1488   TRACE_APPLY ();
1489   return get_subtable ().apply (c, get_type ());
1490 }
1491
1492 inline bool ExtensionPos::sanitize (hb_sanitize_context_t *c)
1493 {
1494   TRACE_SANITIZE ();
1495   if (unlikely (!Extension::sanitize (c))) return false;
1496   unsigned int offset = get_offset ();
1497   if (unlikely (!offset)) return true;
1498   return StructAtOffset<PosLookupSubTable> (this, offset).sanitize (c, get_type ());
1499 }
1500
1501 static inline bool position_lookup (hb_apply_context_t *c, unsigned int lookup_index)
1502 {
1503   const GPOS &gpos = *(c->layout->face->ot_layout->gpos);
1504   const PosLookup &l = gpos.get_lookup (lookup_index);
1505
1506   if (unlikely (c->nesting_level_left == 0))
1507     return false;
1508
1509   if (unlikely (c->context_length < 1))
1510     return false;
1511
1512   return l.apply_once (c->layout, c->buffer, c->lookup_mask, c->context_length, c->nesting_level_left - 1);
1513 }
1514
1515
1516 HB_END_DECLS
1517
1518 #endif /* HB_OT_LAYOUT_GPOS_PRIVATE_HH */