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