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