81e18d48563bec7af07fff3e6d1ab340cfee8664
[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
33 #define HB_OT_LAYOUT_GPOS_NO_LAST ((unsigned int) -1)
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 () const
86   { return _hb_popcount32 ((unsigned int) *this); }
87   inline unsigned int get_size () 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_internal_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_delta (x_ppem) * layout->font->x_scale; else values++;
116     }
117     if (format & yPlaDevice) {
118       if (y_ppem) glyph_pos.y_offset  += (base + get_device (values++)).get_delta (y_ppem) * layout->font->y_scale; else values++;
119     }
120     if (format & xAdvDevice) {
121       if (x_ppem) glyph_pos.x_advance += (base + get_device (values++)).get_delta (x_ppem) * layout->font->x_scale; else values++;
122     }
123     if (format & yAdvDevice) {
124       if (y_ppem) glyph_pos.y_advance += (base + get_device (values++)).get_delta (y_ppem) * layout->font->y_scale; 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 () 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_delta (layout->font->x_ppem) * layout->font->x_scale;
272       if (layout->font->y_ppem)
273         *y += (this+yDeviceTable).get_delta (layout->font->y_ppem) * layout->font->y_scale;
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 (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_internal_glyph_position_t &o = c->buffer->pos[c->buffer->i];
403     o.x_advance = 0;
404     o.y_advance = 0;
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     /* Now comes the messiest part of the whole OpenType
830        specification.  At first glance, cursive connections seem easy
831        to understand, but there are pitfalls!  The reason is that
832        the specs don't mention how to compute the advance values
833        resp. glyph offsets.  I was told it would be an omission, to
834        be fixed in the next OpenType version...  Again many thanks to
835        Andrei Burago <andreib@microsoft.com> for clarifications.
836
837        Consider the following example:
838
839                         |  xadv1    |
840                          +---------+
841                          |         |
842                    +-----+--+ 1    |
843                    |     | .|      |
844                    |    0+--+------+
845                    |   2    |
846                    |        |
847                   0+--------+
848                   |  xadv2   |
849
850          glyph1: advance width = 12
851                  anchor point = (3,1)
852
853          glyph2: advance width = 11
854                  anchor point = (9,4)
855
856          LSB is 1 for both glyphs (so the boxes drawn above are glyph
857          bboxes).  Writing direction is R2L; `0' denotes the glyph's
858          coordinate origin.
859
860        Now the surprising part: The advance width of the *left* glyph
861        (resp. of the *bottom* glyph) will be modified, no matter
862        whether the writing direction is L2R or R2L (resp. T2B or
863        B2T)!  This assymetry is caused by the fact that the glyph's
864        coordinate origin is always the lower left corner for all
865        writing directions.
866
867        Continuing the above example, we can compute the new
868        (horizontal) advance width of glyph2 as
869
870          9 - 3 = 6  ,
871
872        and the new vertical offset of glyph2 as
873
874          1 - 4 = -3  .
875
876
877        Vertical writing direction is far more complicated:
878
879        a) Assuming that we recompute the advance height of the lower glyph:
880
881                                     --
882                          +---------+
883                 --       |         |
884                    +-----+--+ 1    | yadv1
885                    |     | .|      |
886              yadv2 |    0+--+------+        -- BSB1  --
887                    |   2    |       --      --        y_offset
888                    |        |
889      BSB2 --      0+--------+                        --
890           --    --
891
892          glyph1: advance height = 6
893                  anchor point = (3,1)
894
895          glyph2: advance height = 7
896                  anchor point = (9,4)
897
898          TSB is 1 for both glyphs; writing direction is T2B.
899
900
901            BSB1     = yadv1 - (TSB1 + ymax1)
902            BSB2     = yadv2 - (TSB2 + ymax2)
903            y_offset = y2 - y1
904
905          vertical advance width of glyph2
906            = y_offset + BSB2 - BSB1
907            = (y2 - y1) + (yadv2 - (TSB2 + ymax2)) - (yadv1 - (TSB1 + ymax1))
908            = y2 - y1 + yadv2 - TSB2 - ymax2 - (yadv1 - TSB1 - ymax1)
909            = y2 - y1 + yadv2 - TSB2 - ymax2 - yadv1 + TSB1 + ymax1
910
911
912        b) Assuming that we recompute the advance height of the upper glyph:
913
914                                     --      --
915                          +---------+        -- TSB1
916           --    --       |         |
917      TSB2 --       +-----+--+ 1    | yadv1   ymax1
918                    |     | .|      |
919              yadv2 |    0+--+------+        --       --
920       ymax2        |   2    |       --                y_offset
921                    |        |
922           --      0+--------+                        --
923                 --
924
925          glyph1: advance height = 6
926                  anchor point = (3,1)
927
928          glyph2: advance height = 7
929                  anchor point = (9,4)
930
931          TSB is 1 for both glyphs; writing direction is T2B.
932
933          y_offset = y2 - y1
934
935          vertical advance width of glyph2
936            = TSB1 + ymax1 + y_offset - (TSB2 + ymax2)
937            = TSB1 + ymax1 + y2 - y1 - TSB2 - ymax2
938
939
940        Comparing a) with b) shows that b) is easier to compute.  I'll wait
941        for a reply from Andrei to see what should really be implemented...
942
943        Since horizontal advance widths or vertical advance heights
944        can be used alone but not together, no ambiguity occurs.        */
945
946     struct hb_ot_layout_context_t::info_t::gpos_t *gpi = &c->layout->info.gpos;
947     hb_codepoint_t last_pos = gpi->last;
948     gpi->last = HB_OT_LAYOUT_GPOS_NO_LAST;
949
950     /* We don't handle mark glyphs here. */
951     if (c->property == HB_OT_LAYOUT_GLYPH_CLASS_MARK)
952       return false;
953
954     unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoint);
955     if (likely (index == NOT_COVERED))
956       return false;
957
958     const EntryExitRecord &record = entryExitRecord[index];
959
960     if (last_pos == HB_OT_LAYOUT_GPOS_NO_LAST || !record.entryAnchor)
961       goto end;
962
963     hb_position_t entry_x, entry_y;
964     (this+record.entryAnchor).get_anchor (c->layout, c->buffer->info[c->buffer->i].codepoint, &entry_x, &entry_y);
965
966     /* TODO vertical */
967
968     if (c->buffer->direction == HB_DIRECTION_RTL)
969     {
970       /* advance is absolute, not relative */
971       c->buffer->pos[c->buffer->i].x_advance = entry_x - gpi->anchor_x;
972     }
973     else
974     {
975       /* advance is absolute, not relative */
976       c->buffer->pos[last_pos].x_advance = gpi->anchor_x - entry_x;
977     }
978
979     if  (c->lookup_flag & LookupFlag::RightToLeft)
980     {
981       c->buffer->pos[last_pos].cursive_chain = last_pos - c->buffer->i;
982       c->buffer->pos[last_pos].y_offset = entry_y - gpi->anchor_y;
983     }
984     else
985     {
986       c->buffer->pos[c->buffer->i].cursive_chain = c->buffer->i - last_pos;
987       c->buffer->pos[c->buffer->i].y_offset = gpi->anchor_y - entry_y;
988     }
989
990   end:
991     if (record.exitAnchor)
992     {
993       gpi->last = c->buffer->i;
994       (this+record.exitAnchor).get_anchor (c->layout, c->buffer->info[c->buffer->i].codepoint, &gpi->anchor_x, &gpi->anchor_y);
995     }
996
997     c->buffer->i++;
998     return true;
999   }
1000
1001   inline bool sanitize (hb_sanitize_context_t *c) {
1002     TRACE_SANITIZE ();
1003     return coverage.sanitize (c, this)
1004         && entryExitRecord.sanitize (c, this);
1005   }
1006
1007   private:
1008   USHORT        format;                 /* Format identifier--format = 1 */
1009   OffsetTo<Coverage>
1010                 coverage;               /* Offset to Coverage table--from
1011                                          * beginning of subtable */
1012   ArrayOf<EntryExitRecord>
1013                 entryExitRecord;        /* Array of EntryExit records--in
1014                                          * Coverage Index order */
1015   public:
1016   DEFINE_SIZE_ARRAY (6, entryExitRecord);
1017 };
1018
1019 struct CursivePos
1020 {
1021   friend struct PosLookupSubTable;
1022
1023   private:
1024   inline bool apply (hb_apply_context_t *c) const
1025   {
1026     TRACE_APPLY ();
1027     switch (u.format) {
1028     case 1: return u.format1.apply (c);
1029     default:return false;
1030     }
1031   }
1032
1033   inline bool sanitize (hb_sanitize_context_t *c) {
1034     TRACE_SANITIZE ();
1035     if (!u.format.sanitize (c)) return false;
1036     switch (u.format) {
1037     case 1: return u.format1.sanitize (c);
1038     default:return true;
1039     }
1040   }
1041
1042   private:
1043   union {
1044   USHORT                format;         /* Format identifier */
1045   CursivePosFormat1     format1;
1046   } u;
1047 };
1048
1049
1050 typedef AnchorMatrix BaseArray;         /* base-major--
1051                                          * in order of BaseCoverage Index--,
1052                                          * mark-minor--
1053                                          * ordered by class--zero-based. */
1054
1055 struct MarkBasePosFormat1
1056 {
1057   friend struct MarkBasePos;
1058
1059   private:
1060   inline bool apply (hb_apply_context_t *c) const
1061   {
1062     TRACE_APPLY ();
1063     unsigned int mark_index = (this+markCoverage) (c->buffer->info[c->buffer->i].codepoint);
1064     if (likely (mark_index == NOT_COVERED))
1065       return false;
1066
1067     /* now we search backwards for a non-mark glyph */
1068     unsigned int property;
1069     unsigned int j = c->buffer->i;
1070     do
1071     {
1072       if (unlikely (!j))
1073         return false;
1074       j--;
1075     } while (_hb_ot_layout_skip_mark (c->layout->face, &c->buffer->info[j], LookupFlag::IgnoreMarks, &property));
1076
1077     /* The following assertion is too strong, so we've disabled it. */
1078     if (false && !(property & HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH))
1079       return false;
1080
1081     unsigned int base_index = (this+baseCoverage) (c->buffer->info[j].codepoint);
1082     if (base_index == NOT_COVERED)
1083       return false;
1084
1085     return (this+markArray).apply (c, mark_index, base_index, this+baseArray, classCount, j);
1086   }
1087
1088   inline bool sanitize (hb_sanitize_context_t *c) {
1089     TRACE_SANITIZE ();
1090     return c->check_struct (this)
1091         && markCoverage.sanitize (c, this)
1092         && baseCoverage.sanitize (c, this)
1093         && markArray.sanitize (c, this)
1094         && baseArray.sanitize (c, this, (unsigned int) classCount);
1095   }
1096
1097   private:
1098   USHORT        format;                 /* Format identifier--format = 1 */
1099   OffsetTo<Coverage>
1100                 markCoverage;           /* Offset to MarkCoverage table--from
1101                                          * beginning of MarkBasePos subtable */
1102   OffsetTo<Coverage>
1103                 baseCoverage;           /* Offset to BaseCoverage table--from
1104                                          * beginning of MarkBasePos subtable */
1105   USHORT        classCount;             /* Number of classes defined for marks */
1106   OffsetTo<MarkArray>
1107                 markArray;              /* Offset to MarkArray table--from
1108                                          * beginning of MarkBasePos subtable */
1109   OffsetTo<BaseArray>
1110                 baseArray;              /* Offset to BaseArray table--from
1111                                          * beginning of MarkBasePos subtable */
1112   public:
1113   DEFINE_SIZE_STATIC (12);
1114 };
1115
1116 struct MarkBasePos
1117 {
1118   friend struct PosLookupSubTable;
1119
1120   private:
1121   inline bool apply (hb_apply_context_t *c) const
1122   {
1123     TRACE_APPLY ();
1124     switch (u.format) {
1125     case 1: return u.format1.apply (c);
1126     default:return false;
1127     }
1128   }
1129
1130   inline bool sanitize (hb_sanitize_context_t *c) {
1131     TRACE_SANITIZE ();
1132     if (!u.format.sanitize (c)) return false;
1133     switch (u.format) {
1134     case 1: return u.format1.sanitize (c);
1135     default:return true;
1136     }
1137   }
1138
1139   private:
1140   union {
1141   USHORT                format;         /* Format identifier */
1142   MarkBasePosFormat1    format1;
1143   } u;
1144 };
1145
1146
1147 typedef AnchorMatrix LigatureAttach;    /* component-major--
1148                                          * in order of writing direction--,
1149                                          * mark-minor--
1150                                          * ordered by class--zero-based. */
1151
1152 typedef OffsetListOf<LigatureAttach> LigatureArray;
1153                                         /* Array of LigatureAttach
1154                                          * tables ordered by
1155                                          * LigatureCoverage Index */
1156
1157 struct MarkLigPosFormat1
1158 {
1159   friend struct MarkLigPos;
1160
1161   private:
1162   inline bool apply (hb_apply_context_t *c) const
1163   {
1164     TRACE_APPLY ();
1165     unsigned int mark_index = (this+markCoverage) (c->buffer->info[c->buffer->i].codepoint);
1166     if (likely (mark_index == NOT_COVERED))
1167       return false;
1168
1169     /* now we search backwards for a non-mark glyph */
1170     unsigned int property;
1171     unsigned int j = c->buffer->i;
1172     do
1173     {
1174       if (unlikely (!j))
1175         return false;
1176       j--;
1177     } while (_hb_ot_layout_skip_mark (c->layout->face, &c->buffer->info[j], LookupFlag::IgnoreMarks, &property));
1178
1179     /* The following assertion is too strong, so we've disabled it. */
1180     if (false && !(property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE))
1181       return false;
1182
1183     unsigned int lig_index = (this+ligatureCoverage) (c->buffer->info[j].codepoint);
1184     if (lig_index == NOT_COVERED)
1185       return false;
1186
1187     const LigatureArray& lig_array = this+ligatureArray;
1188     const LigatureAttach& lig_attach = lig_array[lig_index];
1189
1190     /* Find component to attach to */
1191     unsigned int comp_count = lig_attach.rows;
1192     if (unlikely (!comp_count))
1193       return false;
1194     unsigned int comp_index;
1195     /* We must now check whether the ligature ID of the current mark glyph
1196      * is identical to the ligature ID of the found ligature.  If yes, we
1197      * can directly use the component index.  If not, we attach the mark
1198      * glyph to the last component of the ligature. */
1199     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)
1200     {
1201       comp_index = c->buffer->info[c->buffer->i].component - 1;
1202       if (comp_index >= comp_count)
1203         comp_index = comp_count - 1;
1204     }
1205     else
1206       comp_index = comp_count - 1;
1207
1208     return (this+markArray).apply (c, mark_index, comp_index, lig_attach, classCount, j);
1209   }
1210
1211   inline bool sanitize (hb_sanitize_context_t *c) {
1212     TRACE_SANITIZE ();
1213     return c->check_struct (this)
1214         && markCoverage.sanitize (c, this)
1215         && ligatureCoverage.sanitize (c, this)
1216         && markArray.sanitize (c, this)
1217         && ligatureArray.sanitize (c, this, (unsigned int) classCount);
1218   }
1219
1220   private:
1221   USHORT        format;                 /* Format identifier--format = 1 */
1222   OffsetTo<Coverage>
1223                 markCoverage;           /* Offset to Mark Coverage table--from
1224                                          * beginning of MarkLigPos subtable */
1225   OffsetTo<Coverage>
1226                 ligatureCoverage;       /* Offset to Ligature Coverage
1227                                          * table--from beginning of MarkLigPos
1228                                          * subtable */
1229   USHORT        classCount;             /* Number of defined mark classes */
1230   OffsetTo<MarkArray>
1231                 markArray;              /* Offset to MarkArray table--from
1232                                          * beginning of MarkLigPos subtable */
1233   OffsetTo<LigatureArray>
1234                 ligatureArray;          /* Offset to LigatureArray table--from
1235                                          * beginning of MarkLigPos subtable */
1236   public:
1237   DEFINE_SIZE_STATIC (12);
1238 };
1239
1240 struct MarkLigPos
1241 {
1242   friend struct PosLookupSubTable;
1243
1244   private:
1245   inline bool apply (hb_apply_context_t *c) const
1246   {
1247     TRACE_APPLY ();
1248     switch (u.format) {
1249     case 1: return u.format1.apply (c);
1250     default:return false;
1251     }
1252   }
1253
1254   inline bool sanitize (hb_sanitize_context_t *c) {
1255     TRACE_SANITIZE ();
1256     if (!u.format.sanitize (c)) return false;
1257     switch (u.format) {
1258     case 1: return u.format1.sanitize (c);
1259     default:return true;
1260     }
1261   }
1262
1263   private:
1264   union {
1265   USHORT                format;         /* Format identifier */
1266   MarkLigPosFormat1     format1;
1267   } u;
1268 };
1269
1270
1271 typedef AnchorMatrix Mark2Array;        /* mark2-major--
1272                                          * in order of Mark2Coverage Index--,
1273                                          * mark1-minor--
1274                                          * ordered by class--zero-based. */
1275
1276 struct MarkMarkPosFormat1
1277 {
1278   friend struct MarkMarkPos;
1279
1280   private:
1281   inline bool apply (hb_apply_context_t *c) const
1282   {
1283     TRACE_APPLY ();
1284     unsigned int mark1_index = (this+mark1Coverage) (c->buffer->info[c->buffer->i].codepoint);
1285     if (likely (mark1_index == NOT_COVERED))
1286       return false;
1287
1288     /* now we search backwards for a suitable mark glyph until a non-mark glyph */
1289     unsigned int property;
1290     unsigned int j = c->buffer->i;
1291     do
1292     {
1293       if (unlikely (!j))
1294         return false;
1295       j--;
1296     } while (_hb_ot_layout_skip_mark (c->layout->face, &c->buffer->info[j], c->lookup_flag, &property));
1297
1298     if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK))
1299       return false;
1300
1301     /* Two marks match only if they belong to the same base, or same component
1302      * of the same ligature.  That is, the component numbers must match, and
1303      * if those are non-zero, the ligid number should also match. */
1304     if ((c->buffer->info[j].component != c->buffer->info[c->buffer->i].component) ||
1305         (c->buffer->info[j].component && c->buffer->info[j].lig_id != c->buffer->info[c->buffer->i].lig_id))
1306       return false;
1307
1308     unsigned int mark2_index = (this+mark2Coverage) (c->buffer->info[j].codepoint);
1309     if (mark2_index == NOT_COVERED)
1310       return false;
1311
1312     return (this+mark1Array).apply (c, mark1_index, mark2_index, this+mark2Array, classCount, j);
1313   }
1314
1315   inline bool sanitize (hb_sanitize_context_t *c) {
1316     TRACE_SANITIZE ();
1317     return c->check_struct (this)
1318         && mark1Coverage.sanitize (c, this)
1319         && mark2Coverage.sanitize (c, this)
1320         && mark1Array.sanitize (c, this)
1321         && mark2Array.sanitize (c, this, (unsigned int) classCount);
1322   }
1323
1324   private:
1325   USHORT        format;                 /* Format identifier--format = 1 */
1326   OffsetTo<Coverage>
1327                 mark1Coverage;          /* Offset to Combining Mark1 Coverage
1328                                          * table--from beginning of MarkMarkPos
1329                                          * subtable */
1330   OffsetTo<Coverage>
1331                 mark2Coverage;          /* Offset to Combining Mark2 Coverage
1332                                          * table--from beginning of MarkMarkPos
1333                                          * subtable */
1334   USHORT        classCount;             /* Number of defined mark classes */
1335   OffsetTo<MarkArray>
1336                 mark1Array;             /* Offset to Mark1Array table--from
1337                                          * beginning of MarkMarkPos subtable */
1338   OffsetTo<Mark2Array>
1339                 mark2Array;             /* Offset to Mark2Array table--from
1340                                          * beginning of MarkMarkPos subtable */
1341   public:
1342   DEFINE_SIZE_STATIC (12);
1343 };
1344
1345 struct MarkMarkPos
1346 {
1347   friend struct PosLookupSubTable;
1348
1349   private:
1350   inline bool apply (hb_apply_context_t *c) const
1351   {
1352     TRACE_APPLY ();
1353     switch (u.format) {
1354     case 1: return u.format1.apply (c);
1355     default:return false;
1356     }
1357   }
1358
1359   inline bool sanitize (hb_sanitize_context_t *c) {
1360     TRACE_SANITIZE ();
1361     if (!u.format.sanitize (c)) return false;
1362     switch (u.format) {
1363     case 1: return u.format1.sanitize (c);
1364     default:return true;
1365     }
1366   }
1367
1368   private:
1369   union {
1370   USHORT                format;         /* Format identifier */
1371   MarkMarkPosFormat1    format1;
1372   } u;
1373 };
1374
1375
1376 static inline bool position_lookup (hb_apply_context_t *c, unsigned int lookup_index);
1377
1378 struct ContextPos : Context
1379 {
1380   friend struct PosLookupSubTable;
1381
1382   private:
1383   inline bool apply (hb_apply_context_t *c) const
1384   {
1385     TRACE_APPLY ();
1386     return Context::apply (c, position_lookup);
1387   }
1388 };
1389
1390 struct ChainContextPos : ChainContext
1391 {
1392   friend struct PosLookupSubTable;
1393
1394   private:
1395   inline bool apply (hb_apply_context_t *c) const
1396   {
1397     TRACE_APPLY ();
1398     return ChainContext::apply (c, position_lookup);
1399   }
1400 };
1401
1402
1403 struct ExtensionPos : Extension
1404 {
1405   friend struct PosLookupSubTable;
1406
1407   private:
1408   inline const struct PosLookupSubTable& get_subtable (void) const
1409   {
1410     unsigned int offset = get_offset ();
1411     if (unlikely (!offset)) return Null(PosLookupSubTable);
1412     return StructAtOffset<PosLookupSubTable> (this, offset);
1413   }
1414
1415   inline bool apply (hb_apply_context_t *c) const;
1416
1417   inline bool sanitize (hb_sanitize_context_t *c);
1418 };
1419
1420
1421
1422 /*
1423  * PosLookup
1424  */
1425
1426
1427 struct PosLookupSubTable
1428 {
1429   friend struct PosLookup;
1430
1431   enum {
1432     Single              = 1,
1433     Pair                = 2,
1434     Cursive             = 3,
1435     MarkBase            = 4,
1436     MarkLig             = 5,
1437     MarkMark            = 6,
1438     Context             = 7,
1439     ChainContext        = 8,
1440     Extension           = 9
1441   };
1442
1443   inline bool apply (hb_apply_context_t *c, unsigned int lookup_type) const
1444   {
1445     TRACE_APPLY ();
1446     switch (lookup_type) {
1447     case Single:                return u.single.apply (c);
1448     case Pair:                  return u.pair.apply (c);
1449     case Cursive:               return u.cursive.apply (c);
1450     case MarkBase:              return u.markBase.apply (c);
1451     case MarkLig:               return u.markLig.apply (c);
1452     case MarkMark:              return u.markMark.apply (c);
1453     case Context:               return u.c.apply (c);
1454     case ChainContext:          return u.chainContext.apply (c);
1455     case Extension:             return u.extension.apply (c);
1456     default:return false;
1457     }
1458   }
1459
1460   inline bool sanitize (hb_sanitize_context_t *c, unsigned int lookup_type) {
1461     TRACE_SANITIZE ();
1462     switch (lookup_type) {
1463     case Single:                return u.single.sanitize (c);
1464     case Pair:                  return u.pair.sanitize (c);
1465     case Cursive:               return u.cursive.sanitize (c);
1466     case MarkBase:              return u.markBase.sanitize (c);
1467     case MarkLig:               return u.markLig.sanitize (c);
1468     case MarkMark:              return u.markMark.sanitize (c);
1469     case Context:               return u.c.sanitize (c);
1470     case ChainContext:          return u.chainContext.sanitize (c);
1471     case Extension:             return u.extension.sanitize (c);
1472     default:return true;
1473     }
1474   }
1475
1476   private:
1477   union {
1478   USHORT                sub_format;
1479   SinglePos             single;
1480   PairPos               pair;
1481   CursivePos            cursive;
1482   MarkBasePos           markBase;
1483   MarkLigPos            markLig;
1484   MarkMarkPos           markMark;
1485   ContextPos            c;
1486   ChainContextPos       chainContext;
1487   ExtensionPos          extension;
1488   } u;
1489   public:
1490   DEFINE_SIZE_UNION (2, sub_format);
1491 };
1492
1493
1494 struct PosLookup : Lookup
1495 {
1496   inline const PosLookupSubTable& get_subtable (unsigned int i) const
1497   { return this+CastR<OffsetArrayOf<PosLookupSubTable> > (subTable)[i]; }
1498
1499   inline bool apply_once (hb_ot_layout_context_t *layout,
1500                           hb_buffer_t    *buffer,
1501                           unsigned int    context_length,
1502                           unsigned int    nesting_level_left) const
1503   {
1504     unsigned int lookup_type = get_type ();
1505     hb_apply_context_t c[1] = {{0}};
1506
1507     c->layout = layout;
1508     c->buffer = buffer;
1509     c->context_length = context_length;
1510     c->nesting_level_left = nesting_level_left;
1511     c->lookup_flag = get_flag ();
1512
1513     if (!_hb_ot_layout_check_glyph_property (c->layout->face, &c->buffer->info[c->buffer->i], c->lookup_flag, &c->property))
1514       return false;
1515
1516     for (unsigned int i = 0; i < get_subtable_count (); i++)
1517       if (get_subtable (i).apply (c, lookup_type))
1518         return true;
1519
1520     return false;
1521   }
1522
1523    inline bool apply_string (hb_ot_layout_context_t *layout,
1524                              hb_buffer_t *buffer,
1525                              hb_mask_t    mask) const
1526   {
1527     bool ret = false;
1528
1529     if (unlikely (!buffer->len))
1530       return false;
1531
1532     layout->info.gpos.last = HB_OT_LAYOUT_GPOS_NO_LAST; /* no last valid glyph for cursive pos. */
1533
1534     buffer->i = 0;
1535     while (buffer->i < buffer->len)
1536     {
1537       bool done;
1538       if (~buffer->info[buffer->i].mask & mask)
1539       {
1540           done = apply_once (layout, buffer, NO_CONTEXT, MAX_NESTING_LEVEL);
1541           ret |= done;
1542       }
1543       else
1544       {
1545           done = false;
1546           /* Contrary to properties defined in GDEF, user-defined properties
1547              will always stop a possible cursive positioning.                */
1548           layout->info.gpos.last = HB_OT_LAYOUT_GPOS_NO_LAST;
1549       }
1550
1551       if (!done)
1552         buffer->i++;
1553     }
1554
1555     return ret;
1556   }
1557
1558   inline bool sanitize (hb_sanitize_context_t *c) {
1559     TRACE_SANITIZE ();
1560     if (unlikely (!Lookup::sanitize (c))) return false;
1561     OffsetArrayOf<PosLookupSubTable> &list = CastR<OffsetArrayOf<PosLookupSubTable> > (subTable);
1562     return list.sanitize (c, this, get_type ());
1563   }
1564 };
1565
1566 typedef OffsetListOf<PosLookup> PosLookupList;
1567
1568 /*
1569  * GPOS
1570  */
1571
1572 struct GPOS : GSUBGPOS
1573 {
1574   static const hb_tag_t Tag     = HB_OT_TAG_GPOS;
1575
1576   inline const PosLookup& get_lookup (unsigned int i) const
1577   { return CastR<PosLookup> (GSUBGPOS::get_lookup (i)); }
1578
1579   inline bool position_lookup (hb_ot_layout_context_t *layout,
1580                                hb_buffer_t  *buffer,
1581                                unsigned int  lookup_index,
1582                                hb_mask_t     mask) const
1583   { return get_lookup (lookup_index).apply_string (layout, buffer, mask); }
1584
1585   inline bool sanitize (hb_sanitize_context_t *c) {
1586     TRACE_SANITIZE ();
1587     if (unlikely (!GSUBGPOS::sanitize (c))) return false;
1588     OffsetTo<PosLookupList> &list = CastR<OffsetTo<PosLookupList> > (lookupList);
1589     return list.sanitize (c, this);
1590   }
1591   public:
1592   DEFINE_SIZE_STATIC (10);
1593 };
1594
1595
1596 /* Out-of-class implementation for methods recursing */
1597
1598 inline bool ExtensionPos::apply (hb_apply_context_t *c) const
1599 {
1600   TRACE_APPLY ();
1601   return get_subtable ().apply (c, get_type ());
1602 }
1603
1604 inline bool ExtensionPos::sanitize (hb_sanitize_context_t *c)
1605 {
1606   TRACE_SANITIZE ();
1607   if (unlikely (!Extension::sanitize (c))) return false;
1608   unsigned int offset = get_offset ();
1609   if (unlikely (!offset)) return true;
1610   return StructAtOffset<PosLookupSubTable> (this, offset).sanitize (c, get_type ());
1611 }
1612
1613 static inline bool position_lookup (hb_apply_context_t *c, unsigned int lookup_index)
1614 {
1615   const GPOS &gpos = *(c->layout->face->ot_layout->gpos);
1616   const PosLookup &l = gpos.get_lookup (lookup_index);
1617
1618   if (unlikely (c->nesting_level_left == 0))
1619     return false;
1620
1621   if (unlikely (c->context_length < 1))
1622     return false;
1623
1624   return l.apply_once (c->layout, c->buffer, c->context_length, c->nesting_level_left - 1);
1625 }
1626
1627
1628 #endif /* HB_OT_LAYOUT_GPOS_PRIVATE_HH */