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