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