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