Home | History | Annotate | Download | only in src
      1 /*
      2  * Copyright  2007,2008,2009,2010  Red Hat, Inc.
      3  * Copyright  2010,2012,2013  Google, Inc.
      4  *
      5  *  This is part of HarfBuzz, a text shaping library.
      6  *
      7  * Permission is hereby granted, without written agreement and without
      8  * license or royalty fees, to use, copy, modify, and distribute this
      9  * software and its documentation for any purpose, provided that the
     10  * above copyright notice and the following two paragraphs appear in
     11  * all copies of this software.
     12  *
     13  * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
     14  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
     15  * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
     16  * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
     17  * DAMAGE.
     18  *
     19  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
     20  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
     21  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
     22  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
     23  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
     24  *
     25  * Red Hat Author(s): Behdad Esfahbod
     26  * Google Author(s): Behdad Esfahbod
     27  */
     28 
     29 #ifndef HB_OT_LAYOUT_GPOS_TABLE_HH
     30 #define HB_OT_LAYOUT_GPOS_TABLE_HH
     31 
     32 #include "hb-ot-layout-gsubgpos.hh"
     33 
     34 
     35 namespace OT {
     36 
     37 
     38 /* buffer **position** var allocations */
     39 #define attach_chain() var.i16[0] /* glyph to which this attaches to, relative to current glyphs; negative for going back, positive for forward. */
     40 #define attach_type() var.u8[2] /* attachment type */
     41 /* Note! if attach_chain() is zero, the value of attach_type() is irrelevant. */
     42 
     43 enum attach_type_t {
     44   ATTACH_TYPE_NONE	= 0X00,
     45 
     46   /* Each attachment should be either a mark or a cursive; can't be both. */
     47   ATTACH_TYPE_MARK	= 0X01,
     48   ATTACH_TYPE_CURSIVE	= 0X02,
     49 };
     50 
     51 
     52 /* Shared Tables: ValueRecord, Anchor Table, and MarkArray */
     53 
     54 typedef HBUINT16 Value;
     55 
     56 typedef UnsizedArrayOf<Value> ValueRecord;
     57 
     58 struct ValueFormat : HBUINT16
     59 {
     60   enum Flags {
     61     xPlacement	= 0x0001u,	/* Includes horizontal adjustment for placement */
     62     yPlacement	= 0x0002u,	/* Includes vertical adjustment for placement */
     63     xAdvance	= 0x0004u,	/* Includes horizontal adjustment for advance */
     64     yAdvance	= 0x0008u,	/* Includes vertical adjustment for advance */
     65     xPlaDevice	= 0x0010u,	/* Includes horizontal Device table for placement */
     66     yPlaDevice	= 0x0020u,	/* Includes vertical Device table for placement */
     67     xAdvDevice	= 0x0040u,	/* Includes horizontal Device table for advance */
     68     yAdvDevice	= 0x0080u,	/* Includes vertical Device table for advance */
     69     ignored	= 0x0F00u,	/* Was used in TrueType Open for MM fonts */
     70     reserved	= 0xF000u,	/* For future use */
     71 
     72     devices	= 0x00F0u	/* Mask for having any Device table */
     73   };
     74 
     75 /* All fields are options.  Only those available advance the value pointer. */
     76 #if 0
     77   HBINT16		xPlacement;		/* Horizontal adjustment for
     78 					 * placement--in design units */
     79   HBINT16		yPlacement;		/* Vertical adjustment for
     80 					 * placement--in design units */
     81   HBINT16		xAdvance;		/* Horizontal adjustment for
     82 					 * advance--in design units (only used
     83 					 * for horizontal writing) */
     84   HBINT16		yAdvance;		/* Vertical adjustment for advance--in
     85 					 * design units (only used for vertical
     86 					 * writing) */
     87   Offset	xPlaDevice;		/* Offset to Device table for
     88 					 * horizontal placement--measured from
     89 					 * beginning of PosTable (may be NULL) */
     90   Offset	yPlaDevice;		/* Offset to Device table for vertical
     91 					 * placement--measured from beginning
     92 					 * of PosTable (may be NULL) */
     93   Offset	xAdvDevice;		/* Offset to Device table for
     94 					 * horizontal advance--measured from
     95 					 * beginning of PosTable (may be NULL) */
     96   Offset	yAdvDevice;		/* Offset to Device table for vertical
     97 					 * advance--measured from beginning of
     98 					 * PosTable (may be NULL) */
     99 #endif
    100 
    101   unsigned int get_len () const  { return hb_popcount ((unsigned int) *this); }
    102   unsigned int get_size () const { return get_len () * Value::static_size; }
    103 
    104   bool apply_value (hb_ot_apply_context_t   *c,
    105 		    const void           *base,
    106 		    const Value          *values,
    107 		    hb_glyph_position_t  &glyph_pos) const
    108   {
    109     bool ret = false;
    110     unsigned int format = *this;
    111     if (!format) return ret;
    112 
    113     hb_font_t *font = c->font;
    114     bool horizontal = HB_DIRECTION_IS_HORIZONTAL (c->direction);
    115 
    116     if (format & xPlacement) glyph_pos.x_offset  += font->em_scale_x (get_short (values++, &ret));
    117     if (format & yPlacement) glyph_pos.y_offset  += font->em_scale_y (get_short (values++, &ret));
    118     if (format & xAdvance) {
    119       if (likely (horizontal)) glyph_pos.x_advance += font->em_scale_x (get_short (values, &ret));
    120       values++;
    121     }
    122     /* y_advance values grow downward but font-space grows upward, hence negation */
    123     if (format & yAdvance) {
    124       if (unlikely (!horizontal)) glyph_pos.y_advance -= font->em_scale_y (get_short (values, &ret));
    125       values++;
    126     }
    127 
    128     if (!has_device ()) return ret;
    129 
    130     bool use_x_device = font->x_ppem || font->num_coords;
    131     bool use_y_device = font->y_ppem || font->num_coords;
    132 
    133     if (!use_x_device && !use_y_device) return ret;
    134 
    135     const VariationStore &store = c->var_store;
    136 
    137     /* pixel -> fractional pixel */
    138     if (format & xPlaDevice) {
    139       if (use_x_device) glyph_pos.x_offset  += (base + get_device (values, &ret)).get_x_delta (font, store);
    140       values++;
    141     }
    142     if (format & yPlaDevice) {
    143       if (use_y_device) glyph_pos.y_offset  += (base + get_device (values, &ret)).get_y_delta (font, store);
    144       values++;
    145     }
    146     if (format & xAdvDevice) {
    147       if (horizontal && use_x_device) glyph_pos.x_advance += (base + get_device (values, &ret)).get_x_delta (font, store);
    148       values++;
    149     }
    150     if (format & yAdvDevice) {
    151       /* y_advance values grow downward but font-space grows upward, hence negation */
    152       if (!horizontal && use_y_device) glyph_pos.y_advance -= (base + get_device (values, &ret)).get_y_delta (font, store);
    153       values++;
    154     }
    155     return ret;
    156   }
    157 
    158   private:
    159   bool sanitize_value_devices (hb_sanitize_context_t *c, const void *base, const Value *values) const
    160   {
    161     unsigned int format = *this;
    162 
    163     if (format & xPlacement) values++;
    164     if (format & yPlacement) values++;
    165     if (format & xAdvance)   values++;
    166     if (format & yAdvance)   values++;
    167 
    168     if ((format & xPlaDevice) && !get_device (values++).sanitize (c, base)) return false;
    169     if ((format & yPlaDevice) && !get_device (values++).sanitize (c, base)) return false;
    170     if ((format & xAdvDevice) && !get_device (values++).sanitize (c, base)) return false;
    171     if ((format & yAdvDevice) && !get_device (values++).sanitize (c, base)) return false;
    172 
    173     return true;
    174   }
    175 
    176   static OffsetTo<Device>& get_device (Value* value)
    177   { return *CastP<OffsetTo<Device> > (value); }
    178   static const OffsetTo<Device>& get_device (const Value* value, bool *worked=nullptr)
    179   {
    180     if (worked) *worked |= *value;
    181     return *CastP<OffsetTo<Device> > (value);
    182   }
    183 
    184   static const HBINT16& get_short (const Value* value, bool *worked=nullptr)
    185   {
    186     if (worked) *worked |= *value;
    187     return *CastP<HBINT16> (value);
    188   }
    189 
    190   public:
    191 
    192   bool has_device () const
    193   {
    194     unsigned int format = *this;
    195     return (format & devices) != 0;
    196   }
    197 
    198   bool sanitize_value (hb_sanitize_context_t *c, const void *base, const Value *values) const
    199   {
    200     TRACE_SANITIZE (this);
    201     return_trace (c->check_range (values, get_size ()) && (!has_device () || sanitize_value_devices (c, base, values)));
    202   }
    203 
    204   bool sanitize_values (hb_sanitize_context_t *c, const void *base, const Value *values, unsigned int count) const
    205   {
    206     TRACE_SANITIZE (this);
    207     unsigned int len = get_len ();
    208 
    209     if (!c->check_range (values, count, get_size ())) return_trace (false);
    210 
    211     if (!has_device ()) return_trace (true);
    212 
    213     for (unsigned int i = 0; i < count; i++) {
    214       if (!sanitize_value_devices (c, base, values))
    215 	return_trace (false);
    216       values += len;
    217     }
    218 
    219     return_trace (true);
    220   }
    221 
    222   /* Just sanitize referenced Device tables.  Doesn't check the values themselves. */
    223   bool sanitize_values_stride_unsafe (hb_sanitize_context_t *c, const void *base, const Value *values, unsigned int count, unsigned int stride) const
    224   {
    225     TRACE_SANITIZE (this);
    226 
    227     if (!has_device ()) return_trace (true);
    228 
    229     for (unsigned int i = 0; i < count; i++) {
    230       if (!sanitize_value_devices (c, base, values))
    231 	return_trace (false);
    232       values += stride;
    233     }
    234 
    235     return_trace (true);
    236   }
    237 };
    238 
    239 
    240 struct AnchorFormat1
    241 {
    242   void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id HB_UNUSED,
    243 		   float *x, float *y) const
    244   {
    245     hb_font_t *font = c->font;
    246     *x = font->em_fscale_x (xCoordinate);
    247     *y = font->em_fscale_y (yCoordinate);
    248   }
    249 
    250   bool sanitize (hb_sanitize_context_t *c) const
    251   {
    252     TRACE_SANITIZE (this);
    253     return_trace (c->check_struct (this));
    254   }
    255 
    256   protected:
    257   HBUINT16	format;			/* Format identifier--format = 1 */
    258   FWORD		xCoordinate;		/* Horizontal value--in design units */
    259   FWORD		yCoordinate;		/* Vertical value--in design units */
    260   public:
    261   DEFINE_SIZE_STATIC (6);
    262 };
    263 
    264 struct AnchorFormat2
    265 {
    266   void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id,
    267 		   float *x, float *y) const
    268   {
    269     hb_font_t *font = c->font;
    270     unsigned int x_ppem = font->x_ppem;
    271     unsigned int y_ppem = font->y_ppem;
    272     hb_position_t cx = 0, cy = 0;
    273     bool ret;
    274 
    275     ret = (x_ppem || y_ppem) &&
    276 	  font->get_glyph_contour_point_for_origin (glyph_id, anchorPoint, HB_DIRECTION_LTR, &cx, &cy);
    277     *x = ret && x_ppem ? cx : font->em_fscale_x (xCoordinate);
    278     *y = ret && y_ppem ? cy : font->em_fscale_y (yCoordinate);
    279   }
    280 
    281   bool sanitize (hb_sanitize_context_t *c) const
    282   {
    283     TRACE_SANITIZE (this);
    284     return_trace (c->check_struct (this));
    285   }
    286 
    287   protected:
    288   HBUINT16	format;			/* Format identifier--format = 2 */
    289   FWORD		xCoordinate;		/* Horizontal value--in design units */
    290   FWORD		yCoordinate;		/* Vertical value--in design units */
    291   HBUINT16	anchorPoint;		/* Index to glyph contour point */
    292   public:
    293   DEFINE_SIZE_STATIC (8);
    294 };
    295 
    296 struct AnchorFormat3
    297 {
    298   void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id HB_UNUSED,
    299 		   float *x, float *y) const
    300   {
    301     hb_font_t *font = c->font;
    302     *x = font->em_fscale_x (xCoordinate);
    303     *y = font->em_fscale_y (yCoordinate);
    304 
    305     if (font->x_ppem || font->num_coords)
    306       *x += (this+xDeviceTable).get_x_delta (font, c->var_store);
    307     if (font->y_ppem || font->num_coords)
    308       *y += (this+yDeviceTable).get_y_delta (font, c->var_store);
    309   }
    310 
    311   bool sanitize (hb_sanitize_context_t *c) const
    312   {
    313     TRACE_SANITIZE (this);
    314     return_trace (c->check_struct (this) && xDeviceTable.sanitize (c, this) && yDeviceTable.sanitize (c, this));
    315   }
    316 
    317   protected:
    318   HBUINT16	format;			/* Format identifier--format = 3 */
    319   FWORD		xCoordinate;		/* Horizontal value--in design units */
    320   FWORD		yCoordinate;		/* Vertical value--in design units */
    321   OffsetTo<Device>
    322 		xDeviceTable;		/* Offset to Device table for X
    323 					 * coordinate-- from beginning of
    324 					 * Anchor table (may be NULL) */
    325   OffsetTo<Device>
    326 		yDeviceTable;		/* Offset to Device table for Y
    327 					 * coordinate-- from beginning of
    328 					 * Anchor table (may be NULL) */
    329   public:
    330   DEFINE_SIZE_STATIC (10);
    331 };
    332 
    333 struct Anchor
    334 {
    335   void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id,
    336 		   float *x, float *y) const
    337   {
    338     *x = *y = 0;
    339     switch (u.format) {
    340     case 1: u.format1.get_anchor (c, glyph_id, x, y); return;
    341     case 2: u.format2.get_anchor (c, glyph_id, x, y); return;
    342     case 3: u.format3.get_anchor (c, glyph_id, x, y); return;
    343     default:					      return;
    344     }
    345   }
    346 
    347   bool sanitize (hb_sanitize_context_t *c) const
    348   {
    349     TRACE_SANITIZE (this);
    350     if (!u.format.sanitize (c)) return_trace (false);
    351     switch (u.format) {
    352     case 1: return_trace (u.format1.sanitize (c));
    353     case 2: return_trace (u.format2.sanitize (c));
    354     case 3: return_trace (u.format3.sanitize (c));
    355     default:return_trace (true);
    356     }
    357   }
    358 
    359   protected:
    360   union {
    361   HBUINT16		format;		/* Format identifier */
    362   AnchorFormat1		format1;
    363   AnchorFormat2		format2;
    364   AnchorFormat3		format3;
    365   } u;
    366   public:
    367   DEFINE_SIZE_UNION (2, format);
    368 };
    369 
    370 
    371 struct AnchorMatrix
    372 {
    373   const Anchor& get_anchor (unsigned int row, unsigned int col,
    374 			    unsigned int cols, bool *found) const
    375   {
    376     *found = false;
    377     if (unlikely (row >= rows || col >= cols)) return Null(Anchor);
    378     *found = !matrixZ[row * cols + col].is_null ();
    379     return this+matrixZ[row * cols + col];
    380   }
    381 
    382   bool sanitize (hb_sanitize_context_t *c, unsigned int cols) const
    383   {
    384     TRACE_SANITIZE (this);
    385     if (!c->check_struct (this)) return_trace (false);
    386     if (unlikely (hb_unsigned_mul_overflows (rows, cols))) return_trace (false);
    387     unsigned int count = rows * cols;
    388     if (!c->check_array (matrixZ.arrayZ, count)) return_trace (false);
    389     for (unsigned int i = 0; i < count; i++)
    390       if (!matrixZ[i].sanitize (c, this)) return_trace (false);
    391     return_trace (true);
    392   }
    393 
    394   HBUINT16	rows;			/* Number of rows */
    395   protected:
    396   UnsizedArrayOf<OffsetTo<Anchor> >
    397 		matrixZ;		/* Matrix of offsets to Anchor tables--
    398 					 * from beginning of AnchorMatrix table */
    399   public:
    400   DEFINE_SIZE_ARRAY (2, matrixZ);
    401 };
    402 
    403 
    404 struct MarkRecord
    405 {
    406   friend struct MarkArray;
    407 
    408   bool sanitize (hb_sanitize_context_t *c, const void *base) const
    409   {
    410     TRACE_SANITIZE (this);
    411     return_trace (c->check_struct (this) && markAnchor.sanitize (c, base));
    412   }
    413 
    414   protected:
    415   HBUINT16	klass;			/* Class defined for this mark */
    416   OffsetTo<Anchor>
    417 		markAnchor;		/* Offset to Anchor table--from
    418 					 * beginning of MarkArray table */
    419   public:
    420   DEFINE_SIZE_STATIC (4);
    421 };
    422 
    423 struct MarkArray : ArrayOf<MarkRecord>	/* Array of MarkRecords--in Coverage order */
    424 {
    425   bool apply (hb_ot_apply_context_t *c,
    426 	      unsigned int mark_index, unsigned int glyph_index,
    427 	      const AnchorMatrix &anchors, unsigned int class_count,
    428 	      unsigned int glyph_pos) const
    429   {
    430     TRACE_APPLY (this);
    431     hb_buffer_t *buffer = c->buffer;
    432     const MarkRecord &record = ArrayOf<MarkRecord>::operator[](mark_index);
    433     unsigned int mark_class = record.klass;
    434 
    435     const Anchor& mark_anchor = this + record.markAnchor;
    436     bool found;
    437     const Anchor& glyph_anchor = anchors.get_anchor (glyph_index, mark_class, class_count, &found);
    438     /* If this subtable doesn't have an anchor for this base and this class,
    439      * return false such that the subsequent subtables have a chance at it. */
    440     if (unlikely (!found)) return_trace (false);
    441 
    442     float mark_x, mark_y, base_x, base_y;
    443 
    444     buffer->unsafe_to_break (glyph_pos, buffer->idx);
    445     mark_anchor.get_anchor (c, buffer->cur().codepoint, &mark_x, &mark_y);
    446     glyph_anchor.get_anchor (c, buffer->info[glyph_pos].codepoint, &base_x, &base_y);
    447 
    448     hb_glyph_position_t &o = buffer->cur_pos();
    449     o.x_offset = round (base_x - mark_x);
    450     o.y_offset = round (base_y - mark_y);
    451     o.attach_type() = ATTACH_TYPE_MARK;
    452     o.attach_chain() = (int) glyph_pos - (int) buffer->idx;
    453     buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
    454 
    455     buffer->idx++;
    456     return_trace (true);
    457   }
    458 
    459   bool sanitize (hb_sanitize_context_t *c) const
    460   {
    461     TRACE_SANITIZE (this);
    462     return_trace (ArrayOf<MarkRecord>::sanitize (c, this));
    463   }
    464 };
    465 
    466 
    467 /* Lookups */
    468 
    469 struct SinglePosFormat1
    470 {
    471   bool intersects (const hb_set_t *glyphs) const
    472   { return (this+coverage).intersects (glyphs); }
    473 
    474   void collect_glyphs (hb_collect_glyphs_context_t *c) const
    475   {
    476     TRACE_COLLECT_GLYPHS (this);
    477     if (unlikely (!(this+coverage).add_coverage (c->input))) return;
    478   }
    479 
    480   const Coverage &get_coverage () const { return this+coverage; }
    481 
    482   bool apply (hb_ot_apply_context_t *c) const
    483   {
    484     TRACE_APPLY (this);
    485     hb_buffer_t *buffer = c->buffer;
    486     unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint);
    487     if (likely (index == NOT_COVERED)) return_trace (false);
    488 
    489     valueFormat.apply_value (c, this, values, buffer->cur_pos());
    490 
    491     buffer->idx++;
    492     return_trace (true);
    493   }
    494 
    495   bool subset (hb_subset_context_t *c) const
    496   {
    497     TRACE_SUBSET (this);
    498     // TODO(subset)
    499     return_trace (false);
    500   }
    501 
    502   bool sanitize (hb_sanitize_context_t *c) const
    503   {
    504     TRACE_SANITIZE (this);
    505     return_trace (c->check_struct (this) &&
    506 		  coverage.sanitize (c, this) &&
    507 		  valueFormat.sanitize_value (c, this, values));
    508   }
    509 
    510   protected:
    511   HBUINT16	format;			/* Format identifier--format = 1 */
    512   OffsetTo<Coverage>
    513 		coverage;		/* Offset to Coverage table--from
    514 					 * beginning of subtable */
    515   ValueFormat	valueFormat;		/* Defines the types of data in the
    516 					 * ValueRecord */
    517   ValueRecord	values;			/* Defines positioning
    518 					 * value(s)--applied to all glyphs in
    519 					 * the Coverage table */
    520   public:
    521   DEFINE_SIZE_ARRAY (6, values);
    522 };
    523 
    524 struct SinglePosFormat2
    525 {
    526   bool intersects (const hb_set_t *glyphs) const
    527   { return (this+coverage).intersects (glyphs); }
    528 
    529   void collect_glyphs (hb_collect_glyphs_context_t *c) const
    530   {
    531     TRACE_COLLECT_GLYPHS (this);
    532     if (unlikely (!(this+coverage).add_coverage (c->input))) return;
    533   }
    534 
    535   const Coverage &get_coverage () const { return this+coverage; }
    536 
    537   bool apply (hb_ot_apply_context_t *c) const
    538   {
    539     TRACE_APPLY (this);
    540     hb_buffer_t *buffer = c->buffer;
    541     unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint);
    542     if (likely (index == NOT_COVERED)) return_trace (false);
    543 
    544     if (likely (index >= valueCount)) return_trace (false);
    545 
    546     valueFormat.apply_value (c, this,
    547 			     &values[index * valueFormat.get_len ()],
    548 			     buffer->cur_pos());
    549 
    550     buffer->idx++;
    551     return_trace (true);
    552   }
    553 
    554   bool subset (hb_subset_context_t *c) const
    555   {
    556     TRACE_SUBSET (this);
    557     // TODO(subset)
    558     return_trace (false);
    559   }
    560 
    561   bool sanitize (hb_sanitize_context_t *c) const
    562   {
    563     TRACE_SANITIZE (this);
    564     return_trace (c->check_struct (this) &&
    565 		  coverage.sanitize (c, this) &&
    566 		  valueFormat.sanitize_values (c, this, values, valueCount));
    567   }
    568 
    569   protected:
    570   HBUINT16	format;			/* Format identifier--format = 2 */
    571   OffsetTo<Coverage>
    572 		coverage;		/* Offset to Coverage table--from
    573 					 * beginning of subtable */
    574   ValueFormat	valueFormat;		/* Defines the types of data in the
    575 					 * ValueRecord */
    576   HBUINT16	valueCount;		/* Number of ValueRecords */
    577   ValueRecord	values;			/* Array of ValueRecords--positioning
    578 					 * values applied to glyphs */
    579   public:
    580   DEFINE_SIZE_ARRAY (8, values);
    581 };
    582 
    583 struct SinglePos
    584 {
    585   template <typename context_t>
    586   typename context_t::return_t dispatch (context_t *c) const
    587   {
    588     TRACE_DISPATCH (this, u.format);
    589     if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
    590     switch (u.format) {
    591     case 1: return_trace (c->dispatch (u.format1));
    592     case 2: return_trace (c->dispatch (u.format2));
    593     default:return_trace (c->default_return_value ());
    594     }
    595   }
    596 
    597   protected:
    598   union {
    599   HBUINT16		format;		/* Format identifier */
    600   SinglePosFormat1	format1;
    601   SinglePosFormat2	format2;
    602   } u;
    603 };
    604 
    605 
    606 struct PairValueRecord
    607 {
    608   friend struct PairSet;
    609 
    610   protected:
    611   GlyphID	secondGlyph;		/* GlyphID of second glyph in the
    612 					 * pair--first glyph is listed in the
    613 					 * Coverage table */
    614   ValueRecord	values;			/* Positioning data for the first glyph
    615 					 * followed by for second glyph */
    616   public:
    617   DEFINE_SIZE_ARRAY (2, values);
    618 };
    619 
    620 struct PairSet
    621 {
    622   friend struct PairPosFormat1;
    623 
    624   bool intersects (const hb_set_t *glyphs,
    625 			  const ValueFormat *valueFormats) const
    626   {
    627     unsigned int len1 = valueFormats[0].get_len ();
    628     unsigned int len2 = valueFormats[1].get_len ();
    629     unsigned int record_size = HBUINT16::static_size * (1 + len1 + len2);
    630 
    631     const PairValueRecord *record = &firstPairValueRecord;
    632     unsigned int count = len;
    633     for (unsigned int i = 0; i < count; i++)
    634     {
    635       if (glyphs->has (record->secondGlyph))
    636 	return true;
    637       record = &StructAtOffset<const PairValueRecord> (record, record_size);
    638     }
    639     return false;
    640   }
    641 
    642   void collect_glyphs (hb_collect_glyphs_context_t *c,
    643 			      const ValueFormat *valueFormats) const
    644   {
    645     TRACE_COLLECT_GLYPHS (this);
    646     unsigned int len1 = valueFormats[0].get_len ();
    647     unsigned int len2 = valueFormats[1].get_len ();
    648     unsigned int record_size = HBUINT16::static_size * (1 + len1 + len2);
    649 
    650     const PairValueRecord *record = &firstPairValueRecord;
    651     c->input->add_array (&record->secondGlyph, len, record_size);
    652   }
    653 
    654   bool apply (hb_ot_apply_context_t *c,
    655 		     const ValueFormat *valueFormats,
    656 		     unsigned int pos) const
    657   {
    658     TRACE_APPLY (this);
    659     hb_buffer_t *buffer = c->buffer;
    660     unsigned int len1 = valueFormats[0].get_len ();
    661     unsigned int len2 = valueFormats[1].get_len ();
    662     unsigned int record_size = HBUINT16::static_size * (1 + len1 + len2);
    663 
    664     unsigned int count = len;
    665 
    666     /* Hand-coded bsearch. */
    667     if (unlikely (!count))
    668       return_trace (false);
    669     hb_codepoint_t x = buffer->info[pos].codepoint;
    670     int min = 0, max = (int) count - 1;
    671     while (min <= max)
    672     {
    673       int mid = ((unsigned int) min + (unsigned int) max) / 2;
    674       const PairValueRecord *record = &StructAtOffset<PairValueRecord> (&firstPairValueRecord, record_size * mid);
    675       hb_codepoint_t mid_x = record->secondGlyph;
    676       if (x < mid_x)
    677 	max = mid - 1;
    678       else if (x > mid_x)
    679 	min = mid + 1;
    680       else
    681       {
    682 	/* Note the intentional use of "|" instead of short-circuit "||". */
    683 	if (valueFormats[0].apply_value (c, this, &record->values[0], buffer->cur_pos()) |
    684 	    valueFormats[1].apply_value (c, this, &record->values[len1], buffer->pos[pos]))
    685 	  buffer->unsafe_to_break (buffer->idx, pos + 1);
    686 	if (len2)
    687 	  pos++;
    688 	buffer->idx = pos;
    689 	return_trace (true);
    690       }
    691     }
    692 
    693     return_trace (false);
    694   }
    695 
    696   struct sanitize_closure_t
    697   {
    698     const void *base;
    699     const ValueFormat *valueFormats;
    700     unsigned int len1; /* valueFormats[0].get_len() */
    701     unsigned int stride; /* 1 + len1 + len2 */
    702   };
    703 
    704   bool sanitize (hb_sanitize_context_t *c, const sanitize_closure_t *closure) const
    705   {
    706     TRACE_SANITIZE (this);
    707     if (!(c->check_struct (this)
    708        && c->check_range (&firstPairValueRecord,
    709 			  len,
    710 			  HBUINT16::static_size,
    711 			  closure->stride))) return_trace (false);
    712 
    713     unsigned int count = len;
    714     const PairValueRecord *record = &firstPairValueRecord;
    715     return_trace (closure->valueFormats[0].sanitize_values_stride_unsafe (c, closure->base, &record->values[0], count, closure->stride) &&
    716 		  closure->valueFormats[1].sanitize_values_stride_unsafe (c, closure->base, &record->values[closure->len1], count, closure->stride));
    717   }
    718 
    719   protected:
    720   HBUINT16		len;	/* Number of PairValueRecords */
    721   PairValueRecord	firstPairValueRecord;
    722 				/* Array of PairValueRecords--ordered
    723 				 * by GlyphID of the second glyph */
    724   public:
    725   DEFINE_SIZE_MIN (2);
    726 };
    727 
    728 struct PairPosFormat1
    729 {
    730   bool intersects (const hb_set_t *glyphs) const
    731   {
    732     unsigned int count = pairSet.len;
    733     for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
    734     {
    735       if (unlikely (iter.get_coverage () >= count))
    736 	break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
    737       if (glyphs->has (iter.get_glyph ()) &&
    738 	  (this+pairSet[iter.get_coverage ()]).intersects (glyphs, valueFormat))
    739 	return true;
    740     }
    741     return false;
    742   }
    743 
    744   void collect_glyphs (hb_collect_glyphs_context_t *c) const
    745   {
    746     TRACE_COLLECT_GLYPHS (this);
    747     if (unlikely (!(this+coverage).add_coverage (c->input))) return;
    748     unsigned int count = pairSet.len;
    749     for (unsigned int i = 0; i < count; i++)
    750       (this+pairSet[i]).collect_glyphs (c, valueFormat);
    751   }
    752 
    753   const Coverage &get_coverage () const { return this+coverage; }
    754 
    755   bool apply (hb_ot_apply_context_t *c) const
    756   {
    757     TRACE_APPLY (this);
    758     hb_buffer_t *buffer = c->buffer;
    759     unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint);
    760     if (likely (index == NOT_COVERED)) return_trace (false);
    761 
    762     hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
    763     skippy_iter.reset (buffer->idx, 1);
    764     if (!skippy_iter.next ()) return_trace (false);
    765 
    766     return_trace ((this+pairSet[index]).apply (c, valueFormat, skippy_iter.idx));
    767   }
    768 
    769   bool subset (hb_subset_context_t *c) const
    770   {
    771     TRACE_SUBSET (this);
    772     // TODO(subset)
    773     return_trace (false);
    774   }
    775 
    776   bool sanitize (hb_sanitize_context_t *c) const
    777   {
    778     TRACE_SANITIZE (this);
    779 
    780     if (!c->check_struct (this)) return_trace (false);
    781 
    782     unsigned int len1 = valueFormat[0].get_len ();
    783     unsigned int len2 = valueFormat[1].get_len ();
    784     PairSet::sanitize_closure_t closure =
    785     {
    786       this,
    787       valueFormat,
    788       len1,
    789       1 + len1 + len2
    790     };
    791 
    792     return_trace (coverage.sanitize (c, this) && pairSet.sanitize (c, this, &closure));
    793   }
    794 
    795   protected:
    796   HBUINT16	format;			/* Format identifier--format = 1 */
    797   OffsetTo<Coverage>
    798 		coverage;		/* Offset to Coverage table--from
    799 					 * beginning of subtable */
    800   ValueFormat	valueFormat[2];		/* [0] Defines the types of data in
    801 					 * ValueRecord1--for the first glyph
    802 					 * in the pair--may be zero (0) */
    803 					/* [1] Defines the types of data in
    804 					 * ValueRecord2--for the second glyph
    805 					 * in the pair--may be zero (0) */
    806   OffsetArrayOf<PairSet>
    807 		pairSet;		/* Array of PairSet tables
    808 					 * ordered by Coverage Index */
    809   public:
    810   DEFINE_SIZE_ARRAY (10, pairSet);
    811 };
    812 
    813 struct PairPosFormat2
    814 {
    815   bool intersects (const hb_set_t *glyphs) const
    816   {
    817     return (this+coverage).intersects (glyphs) &&
    818 	   (this+classDef2).intersects (glyphs);
    819   }
    820 
    821   void collect_glyphs (hb_collect_glyphs_context_t *c) const
    822   {
    823     TRACE_COLLECT_GLYPHS (this);
    824     if (unlikely (!(this+coverage).add_coverage (c->input))) return;
    825     if (unlikely (!(this+classDef2).add_coverage (c->input))) return;
    826   }
    827 
    828   const Coverage &get_coverage () const { return this+coverage; }
    829 
    830   bool apply (hb_ot_apply_context_t *c) const
    831   {
    832     TRACE_APPLY (this);
    833     hb_buffer_t *buffer = c->buffer;
    834     unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint);
    835     if (likely (index == NOT_COVERED)) return_trace (false);
    836 
    837     hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
    838     skippy_iter.reset (buffer->idx, 1);
    839     if (!skippy_iter.next ()) return_trace (false);
    840 
    841     unsigned int len1 = valueFormat1.get_len ();
    842     unsigned int len2 = valueFormat2.get_len ();
    843     unsigned int record_len = len1 + len2;
    844 
    845     unsigned int klass1 = (this+classDef1).get_class (buffer->cur().codepoint);
    846     unsigned int klass2 = (this+classDef2).get_class (buffer->info[skippy_iter.idx].codepoint);
    847     if (unlikely (klass1 >= class1Count || klass2 >= class2Count)) return_trace (false);
    848 
    849     const Value *v = &values[record_len * (klass1 * class2Count + klass2)];
    850     /* Note the intentional use of "|" instead of short-circuit "||". */
    851     if (valueFormat1.apply_value (c, this, v, buffer->cur_pos()) |
    852 	valueFormat2.apply_value (c, this, v + len1, buffer->pos[skippy_iter.idx]))
    853       buffer->unsafe_to_break (buffer->idx, skippy_iter.idx + 1);
    854 
    855     buffer->idx = skippy_iter.idx;
    856     if (len2)
    857       buffer->idx++;
    858 
    859     return_trace (true);
    860   }
    861 
    862   bool subset (hb_subset_context_t *c) const
    863   {
    864     TRACE_SUBSET (this);
    865     // TODO(subset)
    866     return_trace (false);
    867   }
    868 
    869   bool sanitize (hb_sanitize_context_t *c) const
    870   {
    871     TRACE_SANITIZE (this);
    872     if (!(c->check_struct (this)
    873        && coverage.sanitize (c, this)
    874        && classDef1.sanitize (c, this)
    875        && classDef2.sanitize (c, this))) return_trace (false);
    876 
    877     unsigned int len1 = valueFormat1.get_len ();
    878     unsigned int len2 = valueFormat2.get_len ();
    879     unsigned int stride = len1 + len2;
    880     unsigned int record_size = valueFormat1.get_size () + valueFormat2.get_size ();
    881     unsigned int count = (unsigned int) class1Count * (unsigned int) class2Count;
    882     return_trace (c->check_range ((const void *) values,
    883 				  count,
    884 				  record_size) &&
    885 		  valueFormat1.sanitize_values_stride_unsafe (c, this, &values[0], count, stride) &&
    886 		  valueFormat2.sanitize_values_stride_unsafe (c, this, &values[len1], count, stride));
    887   }
    888 
    889   protected:
    890   HBUINT16	format;			/* Format identifier--format = 2 */
    891   OffsetTo<Coverage>
    892 		coverage;		/* Offset to Coverage table--from
    893 					 * beginning of subtable */
    894   ValueFormat	valueFormat1;		/* ValueRecord definition--for the
    895 					 * first glyph of the pair--may be zero
    896 					 * (0) */
    897   ValueFormat	valueFormat2;		/* ValueRecord definition--for the
    898 					 * second glyph of the pair--may be
    899 					 * zero (0) */
    900   OffsetTo<ClassDef>
    901 		classDef1;		/* Offset to ClassDef table--from
    902 					 * beginning of PairPos subtable--for
    903 					 * the first glyph of the pair */
    904   OffsetTo<ClassDef>
    905 		classDef2;		/* Offset to ClassDef table--from
    906 					 * beginning of PairPos subtable--for
    907 					 * the second glyph of the pair */
    908   HBUINT16	class1Count;		/* Number of classes in ClassDef1
    909 					 * table--includes Class0 */
    910   HBUINT16	class2Count;		/* Number of classes in ClassDef2
    911 					 * table--includes Class0 */
    912   ValueRecord	values;			/* Matrix of value pairs:
    913 					 * class1-major, class2-minor,
    914 					 * Each entry has value1 and value2 */
    915   public:
    916   DEFINE_SIZE_ARRAY (16, values);
    917 };
    918 
    919 struct PairPos
    920 {
    921   template <typename context_t>
    922   typename context_t::return_t dispatch (context_t *c) const
    923   {
    924     TRACE_DISPATCH (this, u.format);
    925     if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
    926     switch (u.format) {
    927     case 1: return_trace (c->dispatch (u.format1));
    928     case 2: return_trace (c->dispatch (u.format2));
    929     default:return_trace (c->default_return_value ());
    930     }
    931   }
    932 
    933   protected:
    934   union {
    935   HBUINT16		format;		/* Format identifier */
    936   PairPosFormat1	format1;
    937   PairPosFormat2	format2;
    938   } u;
    939 };
    940 
    941 
    942 struct EntryExitRecord
    943 {
    944   friend struct CursivePosFormat1;
    945 
    946   bool sanitize (hb_sanitize_context_t *c, const void *base) const
    947   {
    948     TRACE_SANITIZE (this);
    949     return_trace (entryAnchor.sanitize (c, base) && exitAnchor.sanitize (c, base));
    950   }
    951 
    952   protected:
    953   OffsetTo<Anchor>
    954 		entryAnchor;		/* Offset to EntryAnchor table--from
    955 					 * beginning of CursivePos
    956 					 * subtable--may be NULL */
    957   OffsetTo<Anchor>
    958 		exitAnchor;		/* Offset to ExitAnchor table--from
    959 					 * beginning of CursivePos
    960 					 * subtable--may be NULL */
    961   public:
    962   DEFINE_SIZE_STATIC (4);
    963 };
    964 
    965 static void
    966 reverse_cursive_minor_offset (hb_glyph_position_t *pos, unsigned int i, hb_direction_t direction, unsigned int new_parent);
    967 
    968 struct CursivePosFormat1
    969 {
    970   bool intersects (const hb_set_t *glyphs) const
    971   { return (this+coverage).intersects (glyphs); }
    972 
    973   void collect_glyphs (hb_collect_glyphs_context_t *c) const
    974   {
    975     TRACE_COLLECT_GLYPHS (this);
    976     if (unlikely (!(this+coverage).add_coverage (c->input))) return;
    977   }
    978 
    979   const Coverage &get_coverage () const { return this+coverage; }
    980 
    981   bool apply (hb_ot_apply_context_t *c) const
    982   {
    983     TRACE_APPLY (this);
    984     hb_buffer_t *buffer = c->buffer;
    985 
    986     const EntryExitRecord &this_record = entryExitRecord[(this+coverage).get_coverage  (buffer->cur().codepoint)];
    987     if (!this_record.entryAnchor) return_trace (false);
    988 
    989     hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
    990     skippy_iter.reset (buffer->idx, 1);
    991     if (!skippy_iter.prev ()) return_trace (false);
    992 
    993     const EntryExitRecord &prev_record = entryExitRecord[(this+coverage).get_coverage  (buffer->info[skippy_iter.idx].codepoint)];
    994     if (!prev_record.exitAnchor) return_trace (false);
    995 
    996     unsigned int i = skippy_iter.idx;
    997     unsigned int j = buffer->idx;
    998 
    999     buffer->unsafe_to_break (i, j);
   1000     float entry_x, entry_y, exit_x, exit_y;
   1001     (this+prev_record.exitAnchor).get_anchor (c, buffer->info[i].codepoint, &exit_x, &exit_y);
   1002     (this+this_record.entryAnchor).get_anchor (c, buffer->info[j].codepoint, &entry_x, &entry_y);
   1003 
   1004     hb_glyph_position_t *pos = buffer->pos;
   1005 
   1006     hb_position_t d;
   1007     /* Main-direction adjustment */
   1008     switch (c->direction) {
   1009       case HB_DIRECTION_LTR:
   1010 	pos[i].x_advance  = round (exit_x) + pos[i].x_offset;
   1011 
   1012 	d = round (entry_x) + pos[j].x_offset;
   1013 	pos[j].x_advance -= d;
   1014 	pos[j].x_offset  -= d;
   1015 	break;
   1016       case HB_DIRECTION_RTL:
   1017 	d = round (exit_x) + pos[i].x_offset;
   1018 	pos[i].x_advance -= d;
   1019 	pos[i].x_offset  -= d;
   1020 
   1021 	pos[j].x_advance  = round (entry_x) + pos[j].x_offset;
   1022 	break;
   1023       case HB_DIRECTION_TTB:
   1024 	pos[i].y_advance  = round (exit_y) + pos[i].y_offset;
   1025 
   1026 	d = round (entry_y) + pos[j].y_offset;
   1027 	pos[j].y_advance -= d;
   1028 	pos[j].y_offset  -= d;
   1029 	break;
   1030       case HB_DIRECTION_BTT:
   1031 	d = round (exit_y) + pos[i].y_offset;
   1032 	pos[i].y_advance -= d;
   1033 	pos[i].y_offset  -= d;
   1034 
   1035 	pos[j].y_advance  = round (entry_y);
   1036 	break;
   1037       case HB_DIRECTION_INVALID:
   1038       default:
   1039 	break;
   1040     }
   1041 
   1042     /* Cross-direction adjustment */
   1043 
   1044     /* We attach child to parent (think graph theory and rooted trees whereas
   1045      * the root stays on baseline and each node aligns itself against its
   1046      * parent.
   1047      *
   1048      * Optimize things for the case of RightToLeft, as that's most common in
   1049      * Arabic. */
   1050     unsigned int child  = i;
   1051     unsigned int parent = j;
   1052     hb_position_t x_offset = entry_x - exit_x;
   1053     hb_position_t y_offset = entry_y - exit_y;
   1054     if  (!(c->lookup_props & LookupFlag::RightToLeft))
   1055     {
   1056       unsigned int k = child;
   1057       child = parent;
   1058       parent = k;
   1059       x_offset = -x_offset;
   1060       y_offset = -y_offset;
   1061     }
   1062 
   1063     /* If child was already connected to someone else, walk through its old
   1064      * chain and reverse the link direction, such that the whole tree of its
   1065      * previous connection now attaches to new parent.  Watch out for case
   1066      * where new parent is on the path from old chain...
   1067      */
   1068     reverse_cursive_minor_offset (pos, child, c->direction, parent);
   1069 
   1070     pos[child].attach_type() = ATTACH_TYPE_CURSIVE;
   1071     pos[child].attach_chain() = (int) parent - (int) child;
   1072     buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
   1073     if (likely (HB_DIRECTION_IS_HORIZONTAL (c->direction)))
   1074       pos[child].y_offset = y_offset;
   1075     else
   1076       pos[child].x_offset = x_offset;
   1077 
   1078     buffer->idx++;
   1079     return_trace (true);
   1080   }
   1081 
   1082   bool subset (hb_subset_context_t *c) const
   1083   {
   1084     TRACE_SUBSET (this);
   1085     // TODO(subset)
   1086     return_trace (false);
   1087   }
   1088 
   1089   bool sanitize (hb_sanitize_context_t *c) const
   1090   {
   1091     TRACE_SANITIZE (this);
   1092     return_trace (coverage.sanitize (c, this) && entryExitRecord.sanitize (c, this));
   1093   }
   1094 
   1095   protected:
   1096   HBUINT16	format;			/* Format identifier--format = 1 */
   1097   OffsetTo<Coverage>
   1098 		coverage;		/* Offset to Coverage table--from
   1099 					 * beginning of subtable */
   1100   ArrayOf<EntryExitRecord>
   1101 		entryExitRecord;	/* Array of EntryExit records--in
   1102 					 * Coverage Index order */
   1103   public:
   1104   DEFINE_SIZE_ARRAY (6, entryExitRecord);
   1105 };
   1106 
   1107 struct CursivePos
   1108 {
   1109   template <typename context_t>
   1110   typename context_t::return_t dispatch (context_t *c) const
   1111   {
   1112     TRACE_DISPATCH (this, u.format);
   1113     if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
   1114     switch (u.format) {
   1115     case 1: return_trace (c->dispatch (u.format1));
   1116     default:return_trace (c->default_return_value ());
   1117     }
   1118   }
   1119 
   1120   protected:
   1121   union {
   1122   HBUINT16		format;		/* Format identifier */
   1123   CursivePosFormat1	format1;
   1124   } u;
   1125 };
   1126 
   1127 
   1128 typedef AnchorMatrix BaseArray;		/* base-major--
   1129 					 * in order of BaseCoverage Index--,
   1130 					 * mark-minor--
   1131 					 * ordered by class--zero-based. */
   1132 
   1133 struct MarkBasePosFormat1
   1134 {
   1135   bool intersects (const hb_set_t *glyphs) const
   1136   { return (this+markCoverage).intersects (glyphs) &&
   1137 	   (this+baseCoverage).intersects (glyphs); }
   1138 
   1139   void collect_glyphs (hb_collect_glyphs_context_t *c) const
   1140   {
   1141     TRACE_COLLECT_GLYPHS (this);
   1142     if (unlikely (!(this+markCoverage).add_coverage (c->input))) return;
   1143     if (unlikely (!(this+baseCoverage).add_coverage (c->input))) return;
   1144   }
   1145 
   1146   const Coverage &get_coverage () const { return this+markCoverage; }
   1147 
   1148   bool apply (hb_ot_apply_context_t *c) const
   1149   {
   1150     TRACE_APPLY (this);
   1151     hb_buffer_t *buffer = c->buffer;
   1152     unsigned int mark_index = (this+markCoverage).get_coverage  (buffer->cur().codepoint);
   1153     if (likely (mark_index == NOT_COVERED)) return_trace (false);
   1154 
   1155     /* Now we search backwards for a non-mark glyph */
   1156     hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
   1157     skippy_iter.reset (buffer->idx, 1);
   1158     skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks);
   1159     do {
   1160       if (!skippy_iter.prev ()) return_trace (false);
   1161       /* We only want to attach to the first of a MultipleSubst sequence.
   1162        * https://github.com/harfbuzz/harfbuzz/issues/740
   1163        * Reject others...
   1164        * ...but stop if we find a mark in the MultipleSubst sequence:
   1165        * https://github.com/harfbuzz/harfbuzz/issues/1020 */
   1166       if (!_hb_glyph_info_multiplied (&buffer->info[skippy_iter.idx]) ||
   1167 	  0 == _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx]) ||
   1168 	  (skippy_iter.idx == 0 ||
   1169 	   _hb_glyph_info_is_mark (&buffer->info[skippy_iter.idx - 1]) ||
   1170 	   _hb_glyph_info_get_lig_id (&buffer->info[skippy_iter.idx]) !=
   1171 	   _hb_glyph_info_get_lig_id (&buffer->info[skippy_iter.idx - 1]) ||
   1172 	   _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx]) !=
   1173 	   _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx - 1]) + 1
   1174 	   ))
   1175 	break;
   1176       skippy_iter.reject ();
   1177     } while (true);
   1178 
   1179     /* Checking that matched glyph is actually a base glyph by GDEF is too strong; disabled */
   1180     //if (!_hb_glyph_info_is_base_glyph (&buffer->info[skippy_iter.idx])) { return_trace (false); }
   1181 
   1182     unsigned int base_index = (this+baseCoverage).get_coverage  (buffer->info[skippy_iter.idx].codepoint);
   1183     if (base_index == NOT_COVERED) return_trace (false);
   1184 
   1185     return_trace ((this+markArray).apply (c, mark_index, base_index, this+baseArray, classCount, skippy_iter.idx));
   1186   }
   1187 
   1188   bool subset (hb_subset_context_t *c) const
   1189   {
   1190     TRACE_SUBSET (this);
   1191     // TODO(subset)
   1192     return_trace (false);
   1193   }
   1194 
   1195   bool sanitize (hb_sanitize_context_t *c) const
   1196   {
   1197     TRACE_SANITIZE (this);
   1198     return_trace (c->check_struct (this) &&
   1199 		  markCoverage.sanitize (c, this) &&
   1200 		  baseCoverage.sanitize (c, this) &&
   1201 		  markArray.sanitize (c, this) &&
   1202 		  baseArray.sanitize (c, this, (unsigned int) classCount));
   1203   }
   1204 
   1205   protected:
   1206   HBUINT16	format;			/* Format identifier--format = 1 */
   1207   OffsetTo<Coverage>
   1208 		markCoverage;		/* Offset to MarkCoverage table--from
   1209 					 * beginning of MarkBasePos subtable */
   1210   OffsetTo<Coverage>
   1211 		baseCoverage;		/* Offset to BaseCoverage table--from
   1212 					 * beginning of MarkBasePos subtable */
   1213   HBUINT16	classCount;		/* Number of classes defined for marks */
   1214   OffsetTo<MarkArray>
   1215 		markArray;		/* Offset to MarkArray table--from
   1216 					 * beginning of MarkBasePos subtable */
   1217   OffsetTo<BaseArray>
   1218 		baseArray;		/* Offset to BaseArray table--from
   1219 					 * beginning of MarkBasePos subtable */
   1220   public:
   1221   DEFINE_SIZE_STATIC (12);
   1222 };
   1223 
   1224 struct MarkBasePos
   1225 {
   1226   template <typename context_t>
   1227   typename context_t::return_t dispatch (context_t *c) const
   1228   {
   1229     TRACE_DISPATCH (this, u.format);
   1230     if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
   1231     switch (u.format) {
   1232     case 1: return_trace (c->dispatch (u.format1));
   1233     default:return_trace (c->default_return_value ());
   1234     }
   1235   }
   1236 
   1237   protected:
   1238   union {
   1239   HBUINT16		format;		/* Format identifier */
   1240   MarkBasePosFormat1	format1;
   1241   } u;
   1242 };
   1243 
   1244 
   1245 typedef AnchorMatrix LigatureAttach;	/* component-major--
   1246 					 * in order of writing direction--,
   1247 					 * mark-minor--
   1248 					 * ordered by class--zero-based. */
   1249 
   1250 typedef OffsetListOf<LigatureAttach> LigatureArray;
   1251 					/* Array of LigatureAttach
   1252 					 * tables ordered by
   1253 					 * LigatureCoverage Index */
   1254 
   1255 struct MarkLigPosFormat1
   1256 {
   1257   bool intersects (const hb_set_t *glyphs) const
   1258   { return (this+markCoverage).intersects (glyphs) &&
   1259 	   (this+ligatureCoverage).intersects (glyphs); }
   1260 
   1261   void collect_glyphs (hb_collect_glyphs_context_t *c) const
   1262   {
   1263     TRACE_COLLECT_GLYPHS (this);
   1264     if (unlikely (!(this+markCoverage).add_coverage (c->input))) return;
   1265     if (unlikely (!(this+ligatureCoverage).add_coverage (c->input))) return;
   1266   }
   1267 
   1268   const Coverage &get_coverage () const { return this+markCoverage; }
   1269 
   1270   bool apply (hb_ot_apply_context_t *c) const
   1271   {
   1272     TRACE_APPLY (this);
   1273     hb_buffer_t *buffer = c->buffer;
   1274     unsigned int mark_index = (this+markCoverage).get_coverage  (buffer->cur().codepoint);
   1275     if (likely (mark_index == NOT_COVERED)) return_trace (false);
   1276 
   1277     /* Now we search backwards for a non-mark glyph */
   1278     hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
   1279     skippy_iter.reset (buffer->idx, 1);
   1280     skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks);
   1281     if (!skippy_iter.prev ()) return_trace (false);
   1282 
   1283     /* Checking that matched glyph is actually a ligature by GDEF is too strong; disabled */
   1284     //if (!_hb_glyph_info_is_ligature (&buffer->info[skippy_iter.idx])) { return_trace (false); }
   1285 
   1286     unsigned int j = skippy_iter.idx;
   1287     unsigned int lig_index = (this+ligatureCoverage).get_coverage  (buffer->info[j].codepoint);
   1288     if (lig_index == NOT_COVERED) return_trace (false);
   1289 
   1290     const LigatureArray& lig_array = this+ligatureArray;
   1291     const LigatureAttach& lig_attach = lig_array[lig_index];
   1292 
   1293     /* Find component to attach to */
   1294     unsigned int comp_count = lig_attach.rows;
   1295     if (unlikely (!comp_count)) return_trace (false);
   1296 
   1297     /* We must now check whether the ligature ID of the current mark glyph
   1298      * is identical to the ligature ID of the found ligature.  If yes, we
   1299      * can directly use the component index.  If not, we attach the mark
   1300      * glyph to the last component of the ligature. */
   1301     unsigned int comp_index;
   1302     unsigned int lig_id = _hb_glyph_info_get_lig_id (&buffer->info[j]);
   1303     unsigned int mark_id = _hb_glyph_info_get_lig_id (&buffer->cur());
   1304     unsigned int mark_comp = _hb_glyph_info_get_lig_comp (&buffer->cur());
   1305     if (lig_id && lig_id == mark_id && mark_comp > 0)
   1306       comp_index = MIN (comp_count, _hb_glyph_info_get_lig_comp (&buffer->cur())) - 1;
   1307     else
   1308       comp_index = comp_count - 1;
   1309 
   1310     return_trace ((this+markArray).apply (c, mark_index, comp_index, lig_attach, classCount, j));
   1311   }
   1312 
   1313   bool subset (hb_subset_context_t *c) const
   1314   {
   1315     TRACE_SUBSET (this);
   1316     // TODO(subset)
   1317     return_trace (false);
   1318   }
   1319 
   1320   bool sanitize (hb_sanitize_context_t *c) const
   1321   {
   1322     TRACE_SANITIZE (this);
   1323     return_trace (c->check_struct (this) &&
   1324 		  markCoverage.sanitize (c, this) &&
   1325 		  ligatureCoverage.sanitize (c, this) &&
   1326 		  markArray.sanitize (c, this) &&
   1327 		  ligatureArray.sanitize (c, this, (unsigned int) classCount));
   1328   }
   1329 
   1330   protected:
   1331   HBUINT16	format;			/* Format identifier--format = 1 */
   1332   OffsetTo<Coverage>
   1333 		markCoverage;		/* Offset to Mark Coverage table--from
   1334 					 * beginning of MarkLigPos subtable */
   1335   OffsetTo<Coverage>
   1336 		ligatureCoverage;	/* Offset to Ligature Coverage
   1337 					 * table--from beginning of MarkLigPos
   1338 					 * subtable */
   1339   HBUINT16	classCount;		/* Number of defined mark classes */
   1340   OffsetTo<MarkArray>
   1341 		markArray;		/* Offset to MarkArray table--from
   1342 					 * beginning of MarkLigPos subtable */
   1343   OffsetTo<LigatureArray>
   1344 		ligatureArray;		/* Offset to LigatureArray table--from
   1345 					 * beginning of MarkLigPos subtable */
   1346   public:
   1347   DEFINE_SIZE_STATIC (12);
   1348 };
   1349 
   1350 struct MarkLigPos
   1351 {
   1352   template <typename context_t>
   1353   typename context_t::return_t dispatch (context_t *c) const
   1354   {
   1355     TRACE_DISPATCH (this, u.format);
   1356     if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
   1357     switch (u.format) {
   1358     case 1: return_trace (c->dispatch (u.format1));
   1359     default:return_trace (c->default_return_value ());
   1360     }
   1361   }
   1362 
   1363   protected:
   1364   union {
   1365   HBUINT16		format;		/* Format identifier */
   1366   MarkLigPosFormat1	format1;
   1367   } u;
   1368 };
   1369 
   1370 
   1371 typedef AnchorMatrix Mark2Array;	/* mark2-major--
   1372 					 * in order of Mark2Coverage Index--,
   1373 					 * mark1-minor--
   1374 					 * ordered by class--zero-based. */
   1375 
   1376 struct MarkMarkPosFormat1
   1377 {
   1378   bool intersects (const hb_set_t *glyphs) const
   1379   { return (this+mark1Coverage).intersects (glyphs) &&
   1380 	   (this+mark2Coverage).intersects (glyphs); }
   1381 
   1382   void collect_glyphs (hb_collect_glyphs_context_t *c) const
   1383   {
   1384     TRACE_COLLECT_GLYPHS (this);
   1385     if (unlikely (!(this+mark1Coverage).add_coverage (c->input))) return;
   1386     if (unlikely (!(this+mark2Coverage).add_coverage (c->input))) return;
   1387   }
   1388 
   1389   const Coverage &get_coverage () const { return this+mark1Coverage; }
   1390 
   1391   bool apply (hb_ot_apply_context_t *c) const
   1392   {
   1393     TRACE_APPLY (this);
   1394     hb_buffer_t *buffer = c->buffer;
   1395     unsigned int mark1_index = (this+mark1Coverage).get_coverage  (buffer->cur().codepoint);
   1396     if (likely (mark1_index == NOT_COVERED)) return_trace (false);
   1397 
   1398     /* now we search backwards for a suitable mark glyph until a non-mark glyph */
   1399     hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
   1400     skippy_iter.reset (buffer->idx, 1);
   1401     skippy_iter.set_lookup_props (c->lookup_props & ~LookupFlag::IgnoreFlags);
   1402     if (!skippy_iter.prev ()) return_trace (false);
   1403 
   1404     if (!_hb_glyph_info_is_mark (&buffer->info[skippy_iter.idx])) { return_trace (false); }
   1405 
   1406     unsigned int j = skippy_iter.idx;
   1407 
   1408     unsigned int id1 = _hb_glyph_info_get_lig_id (&buffer->cur());
   1409     unsigned int id2 = _hb_glyph_info_get_lig_id (&buffer->info[j]);
   1410     unsigned int comp1 = _hb_glyph_info_get_lig_comp (&buffer->cur());
   1411     unsigned int comp2 = _hb_glyph_info_get_lig_comp (&buffer->info[j]);
   1412 
   1413     if (likely (id1 == id2)) {
   1414       if (id1 == 0) /* Marks belonging to the same base. */
   1415 	goto good;
   1416       else if (comp1 == comp2) /* Marks belonging to the same ligature component. */
   1417 	goto good;
   1418     } else {
   1419       /* If ligature ids don't match, it may be the case that one of the marks
   1420        * itself is a ligature.  In which case match. */
   1421       if ((id1 > 0 && !comp1) || (id2 > 0 && !comp2))
   1422 	goto good;
   1423     }
   1424 
   1425     /* Didn't match. */
   1426     return_trace (false);
   1427 
   1428     good:
   1429     unsigned int mark2_index = (this+mark2Coverage).get_coverage  (buffer->info[j].codepoint);
   1430     if (mark2_index == NOT_COVERED) return_trace (false);
   1431 
   1432     return_trace ((this+mark1Array).apply (c, mark1_index, mark2_index, this+mark2Array, classCount, j));
   1433   }
   1434 
   1435   bool subset (hb_subset_context_t *c) const
   1436   {
   1437     TRACE_SUBSET (this);
   1438     // TODO(subset)
   1439     return_trace (false);
   1440   }
   1441 
   1442   bool sanitize (hb_sanitize_context_t *c) const
   1443   {
   1444     TRACE_SANITIZE (this);
   1445     return_trace (c->check_struct (this) &&
   1446 		  mark1Coverage.sanitize (c, this) &&
   1447 		  mark2Coverage.sanitize (c, this) &&
   1448 		  mark1Array.sanitize (c, this) &&
   1449 		  mark2Array.sanitize (c, this, (unsigned int) classCount));
   1450   }
   1451 
   1452   protected:
   1453   HBUINT16	format;			/* Format identifier--format = 1 */
   1454   OffsetTo<Coverage>
   1455 		mark1Coverage;		/* Offset to Combining Mark1 Coverage
   1456 					 * table--from beginning of MarkMarkPos
   1457 					 * subtable */
   1458   OffsetTo<Coverage>
   1459 		mark2Coverage;		/* Offset to Combining Mark2 Coverage
   1460 					 * table--from beginning of MarkMarkPos
   1461 					 * subtable */
   1462   HBUINT16	classCount;		/* Number of defined mark classes */
   1463   OffsetTo<MarkArray>
   1464 		mark1Array;		/* Offset to Mark1Array table--from
   1465 					 * beginning of MarkMarkPos subtable */
   1466   OffsetTo<Mark2Array>
   1467 		mark2Array;		/* Offset to Mark2Array table--from
   1468 					 * beginning of MarkMarkPos subtable */
   1469   public:
   1470   DEFINE_SIZE_STATIC (12);
   1471 };
   1472 
   1473 struct MarkMarkPos
   1474 {
   1475   template <typename context_t>
   1476   typename context_t::return_t dispatch (context_t *c) const
   1477   {
   1478     TRACE_DISPATCH (this, u.format);
   1479     if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
   1480     switch (u.format) {
   1481     case 1: return_trace (c->dispatch (u.format1));
   1482     default:return_trace (c->default_return_value ());
   1483     }
   1484   }
   1485 
   1486   protected:
   1487   union {
   1488   HBUINT16		format;		/* Format identifier */
   1489   MarkMarkPosFormat1	format1;
   1490   } u;
   1491 };
   1492 
   1493 
   1494 struct ContextPos : Context {};
   1495 
   1496 struct ChainContextPos : ChainContext {};
   1497 
   1498 struct ExtensionPos : Extension<ExtensionPos>
   1499 {
   1500   typedef struct PosLookupSubTable SubTable;
   1501 };
   1502 
   1503 
   1504 
   1505 /*
   1506  * PosLookup
   1507  */
   1508 
   1509 
   1510 struct PosLookupSubTable
   1511 {
   1512   friend struct Lookup;
   1513   friend struct PosLookup;
   1514 
   1515   enum Type {
   1516     Single		= 1,
   1517     Pair		= 2,
   1518     Cursive		= 3,
   1519     MarkBase		= 4,
   1520     MarkLig		= 5,
   1521     MarkMark		= 6,
   1522     Context		= 7,
   1523     ChainContext	= 8,
   1524     Extension		= 9
   1525   };
   1526 
   1527   template <typename context_t>
   1528   typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type) const
   1529   {
   1530     TRACE_DISPATCH (this, lookup_type);
   1531     switch (lookup_type) {
   1532     case Single:		return_trace (u.single.dispatch (c));
   1533     case Pair:			return_trace (u.pair.dispatch (c));
   1534     case Cursive:		return_trace (u.cursive.dispatch (c));
   1535     case MarkBase:		return_trace (u.markBase.dispatch (c));
   1536     case MarkLig:		return_trace (u.markLig.dispatch (c));
   1537     case MarkMark:		return_trace (u.markMark.dispatch (c));
   1538     case Context:		return_trace (u.context.dispatch (c));
   1539     case ChainContext:		return_trace (u.chainContext.dispatch (c));
   1540     case Extension:		return_trace (u.extension.dispatch (c));
   1541     default:			return_trace (c->default_return_value ());
   1542     }
   1543   }
   1544 
   1545   protected:
   1546   union {
   1547   SinglePos		single;
   1548   PairPos		pair;
   1549   CursivePos		cursive;
   1550   MarkBasePos		markBase;
   1551   MarkLigPos		markLig;
   1552   MarkMarkPos		markMark;
   1553   ContextPos		context;
   1554   ChainContextPos	chainContext;
   1555   ExtensionPos		extension;
   1556   } u;
   1557   public:
   1558   DEFINE_SIZE_MIN (0);
   1559 };
   1560 
   1561 
   1562 struct PosLookup : Lookup
   1563 {
   1564   typedef struct PosLookupSubTable SubTable;
   1565 
   1566   const SubTable& get_subtable (unsigned int i) const
   1567   { return Lookup::get_subtable<SubTable> (i); }
   1568 
   1569   bool is_reverse () const
   1570   {
   1571     return false;
   1572   }
   1573 
   1574   bool apply (hb_ot_apply_context_t *c) const
   1575   {
   1576     TRACE_APPLY (this);
   1577     return_trace (dispatch (c));
   1578   }
   1579 
   1580   bool intersects (const hb_set_t *glyphs) const
   1581   {
   1582     hb_intersects_context_t c (glyphs);
   1583     return dispatch (&c);
   1584   }
   1585 
   1586   hb_collect_glyphs_context_t::return_t collect_glyphs (hb_collect_glyphs_context_t *c) const
   1587   {
   1588     TRACE_COLLECT_GLYPHS (this);
   1589     return_trace (dispatch (c));
   1590   }
   1591 
   1592   template <typename set_t>
   1593   void add_coverage (set_t *glyphs) const
   1594   {
   1595     hb_add_coverage_context_t<set_t> c (glyphs);
   1596     dispatch (&c);
   1597   }
   1598 
   1599   static bool apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index);
   1600 
   1601   template <typename context_t>
   1602   static typename context_t::return_t dispatch_recurse_func (context_t *c, unsigned int lookup_index);
   1603 
   1604   template <typename context_t>
   1605   typename context_t::return_t dispatch (context_t *c) const
   1606   { return Lookup::dispatch<SubTable> (c); }
   1607 
   1608   bool subset (hb_subset_context_t *c) const
   1609   { return Lookup::subset<SubTable> (c); }
   1610 
   1611   bool sanitize (hb_sanitize_context_t *c) const
   1612   { return Lookup::sanitize<SubTable> (c); }
   1613 };
   1614 
   1615 /*
   1616  * GPOS -- Glyph Positioning
   1617  * https://docs.microsoft.com/en-us/typography/opentype/spec/gpos
   1618  */
   1619 
   1620 struct GPOS : GSUBGPOS
   1621 {
   1622   enum { tableTag = HB_OT_TAG_GPOS };
   1623 
   1624   const PosLookup& get_lookup (unsigned int i) const
   1625   { return CastR<PosLookup> (GSUBGPOS::get_lookup (i)); }
   1626 
   1627   static inline void position_start (hb_font_t *font, hb_buffer_t *buffer);
   1628   static inline void position_finish_advances (hb_font_t *font, hb_buffer_t *buffer);
   1629   static inline void position_finish_offsets (hb_font_t *font, hb_buffer_t *buffer);
   1630 
   1631   bool subset (hb_subset_context_t *c) const
   1632   { return GSUBGPOS::subset<PosLookup> (c); }
   1633 
   1634   bool sanitize (hb_sanitize_context_t *c) const
   1635   { return GSUBGPOS::sanitize<PosLookup> (c); }
   1636 
   1637   HB_INTERNAL bool is_blacklisted (hb_blob_t *blob,
   1638 				   hb_face_t *face) const;
   1639 
   1640   typedef GSUBGPOS::accelerator_t<GPOS> accelerator_t;
   1641 };
   1642 
   1643 
   1644 static void
   1645 reverse_cursive_minor_offset (hb_glyph_position_t *pos, unsigned int i, hb_direction_t direction, unsigned int new_parent)
   1646 {
   1647   int chain = pos[i].attach_chain(), type = pos[i].attach_type();
   1648   if (likely (!chain || 0 == (type & ATTACH_TYPE_CURSIVE)))
   1649     return;
   1650 
   1651   pos[i].attach_chain() = 0;
   1652 
   1653   unsigned int j = (int) i + chain;
   1654 
   1655   /* Stop if we see new parent in the chain. */
   1656   if (j == new_parent)
   1657     return;
   1658 
   1659   reverse_cursive_minor_offset (pos, j, direction, new_parent);
   1660 
   1661   if (HB_DIRECTION_IS_HORIZONTAL (direction))
   1662     pos[j].y_offset = -pos[i].y_offset;
   1663   else
   1664     pos[j].x_offset = -pos[i].x_offset;
   1665 
   1666   pos[j].attach_chain() = -chain;
   1667   pos[j].attach_type() = type;
   1668 }
   1669 static void
   1670 propagate_attachment_offsets (hb_glyph_position_t *pos,
   1671 			      unsigned int len,
   1672 			      unsigned int i,
   1673 			      hb_direction_t direction)
   1674 {
   1675   /* Adjusts offsets of attached glyphs (both cursive and mark) to accumulate
   1676    * offset of glyph they are attached to. */
   1677   int chain = pos[i].attach_chain(), type = pos[i].attach_type();
   1678   if (likely (!chain))
   1679     return;
   1680 
   1681   pos[i].attach_chain() = 0;
   1682 
   1683   unsigned int j = (int) i + chain;
   1684 
   1685   if (unlikely (j >= len))
   1686     return;
   1687 
   1688   propagate_attachment_offsets (pos, len, j, direction);
   1689 
   1690   assert (!!(type & ATTACH_TYPE_MARK) ^ !!(type & ATTACH_TYPE_CURSIVE));
   1691 
   1692   if (type & ATTACH_TYPE_CURSIVE)
   1693   {
   1694     if (HB_DIRECTION_IS_HORIZONTAL (direction))
   1695       pos[i].y_offset += pos[j].y_offset;
   1696     else
   1697       pos[i].x_offset += pos[j].x_offset;
   1698   }
   1699   else /*if (type & ATTACH_TYPE_MARK)*/
   1700   {
   1701     pos[i].x_offset += pos[j].x_offset;
   1702     pos[i].y_offset += pos[j].y_offset;
   1703 
   1704     assert (j < i);
   1705     if (HB_DIRECTION_IS_FORWARD (direction))
   1706       for (unsigned int k = j; k < i; k++) {
   1707 	pos[i].x_offset -= pos[k].x_advance;
   1708 	pos[i].y_offset -= pos[k].y_advance;
   1709       }
   1710     else
   1711       for (unsigned int k = j + 1; k < i + 1; k++) {
   1712 	pos[i].x_offset += pos[k].x_advance;
   1713 	pos[i].y_offset += pos[k].y_advance;
   1714       }
   1715   }
   1716 }
   1717 
   1718 void
   1719 GPOS::position_start (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer)
   1720 {
   1721   unsigned int count = buffer->len;
   1722   for (unsigned int i = 0; i < count; i++)
   1723     buffer->pos[i].attach_chain() = buffer->pos[i].attach_type() = 0;
   1724 }
   1725 
   1726 void
   1727 GPOS::position_finish_advances (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer HB_UNUSED)
   1728 {
   1729   //_hb_buffer_assert_gsubgpos_vars (buffer);
   1730 }
   1731 
   1732 void
   1733 GPOS::position_finish_offsets (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer)
   1734 {
   1735   _hb_buffer_assert_gsubgpos_vars (buffer);
   1736 
   1737   unsigned int len;
   1738   hb_glyph_position_t *pos = hb_buffer_get_glyph_positions (buffer, &len);
   1739   hb_direction_t direction = buffer->props.direction;
   1740 
   1741   /* Handle attachments */
   1742   if (buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT)
   1743     for (unsigned int i = 0; i < len; i++)
   1744       propagate_attachment_offsets (pos, len, i, direction);
   1745 }
   1746 
   1747 
   1748 struct GPOS_accelerator_t : GPOS::accelerator_t {};
   1749 
   1750 
   1751 /* Out-of-class implementation for methods recursing */
   1752 
   1753 template <typename context_t>
   1754 /*static*/ inline typename context_t::return_t PosLookup::dispatch_recurse_func (context_t *c, unsigned int lookup_index)
   1755 {
   1756   const PosLookup &l = c->face->table.GPOS.get_relaxed ()->table->get_lookup (lookup_index);
   1757   return l.dispatch (c);
   1758 }
   1759 
   1760 /*static*/ inline bool PosLookup::apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index)
   1761 {
   1762   const PosLookup &l = c->face->table.GPOS.get_relaxed ()->table->get_lookup (lookup_index);
   1763   unsigned int saved_lookup_props = c->lookup_props;
   1764   unsigned int saved_lookup_index = c->lookup_index;
   1765   c->set_lookup_index (lookup_index);
   1766   c->set_lookup_props (l.get_props ());
   1767   bool ret = l.dispatch (c);
   1768   c->set_lookup_index (saved_lookup_index);
   1769   c->set_lookup_props (saved_lookup_props);
   1770   return ret;
   1771 }
   1772 
   1773 
   1774 } /* namespace OT */
   1775 
   1776 
   1777 #endif /* HB_OT_LAYOUT_GPOS_TABLE_HH */
   1778