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