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