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