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