tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

hb-aat-layout-kerx-table.hh (35501B)


      1 /*
      2 * Copyright © 2018  Ebrahim Byagowi
      3 * Copyright © 2018  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 * Google Author(s): Behdad Esfahbod
     26 */
     27 
     28 #ifndef HB_AAT_LAYOUT_KERX_TABLE_HH
     29 #define HB_AAT_LAYOUT_KERX_TABLE_HH
     30 
     31 #include "hb-kern.hh"
     32 #include "hb-aat-layout-ankr-table.hh"
     33 #include "hb-set-digest.hh"
     34 
     35 /*
     36 * kerx -- Extended Kerning
     37 * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6kerx.html
     38 */
     39 #define HB_AAT_TAG_kerx HB_TAG('k','e','r','x')
     40 
     41 
     42 namespace AAT {
     43 
     44 using namespace OT;
     45 
     46 
     47 static inline int
     48 kerxTupleKern (int value,
     49        unsigned int tupleCount,
     50        const void *base,
     51        hb_aat_apply_context_t *c)
     52 {
     53  if (likely (!tupleCount || !c)) return value;
     54 
     55  unsigned int offset = value;
     56  const FWORD *pv = &StructAtOffset<FWORD> (base, offset);
     57  if (unlikely (!c->sanitizer.check_array (pv, tupleCount))) return 0;
     58  hb_barrier ();
     59  return *pv;
     60 }
     61 
     62 
     63 struct hb_glyph_pair_t
     64 {
     65  hb_codepoint_t left;
     66  hb_codepoint_t right;
     67 };
     68 
     69 struct KernPair
     70 {
     71  int get_kerning () const { return value; }
     72 
     73  int cmp (const hb_glyph_pair_t &o) const
     74  {
     75    int ret = left.cmp (o.left);
     76    if (ret) return ret;
     77    return right.cmp (o.right);
     78  }
     79 
     80  bool sanitize (hb_sanitize_context_t *c) const
     81  {
     82    TRACE_SANITIZE (this);
     83    return_trace (c->check_struct (this));
     84  }
     85 
     86  public:
     87  HBGlyphID16	left;
     88  HBGlyphID16	right;
     89  FWORD		value;
     90  public:
     91  DEFINE_SIZE_STATIC (6);
     92 };
     93 
     94 template <typename KernSubTableHeader>
     95 struct KerxSubTableFormat0
     96 {
     97  int get_kerning (hb_codepoint_t left, hb_codepoint_t right,
     98 	   hb_aat_apply_context_t *c = nullptr) const
     99  {
    100    hb_glyph_pair_t pair = {left, right};
    101    int v = pairs.bsearch (pair).get_kerning ();
    102    return kerxTupleKern (v, header.tuple_count (), this, c);
    103  }
    104 
    105  bool apply (hb_aat_apply_context_t *c) const
    106  {
    107    TRACE_APPLY (this);
    108 
    109    if (!c->plan->requested_kerning)
    110      return_trace (false);
    111 
    112    if (header.coverage & header.Backwards)
    113      return_trace (false);
    114 
    115    accelerator_t accel (*this, c);
    116    hb_kern_machine_t<accelerator_t> machine (accel, header.coverage & header.CrossStream);
    117    machine.kern (c->font, c->buffer, c->plan->kern_mask);
    118 
    119    return_trace (true);
    120  }
    121 
    122  template <typename set_t>
    123  void collect_glyphs (set_t &first_set, set_t &second_set, unsigned num_glyphs) const
    124  {
    125    for (const KernPair& pair : pairs)
    126    {
    127      first_set.add (pair.left);
    128      second_set.add (pair.right);
    129    }
    130  }
    131 
    132  struct accelerator_t
    133  {
    134    const KerxSubTableFormat0 &table;
    135    hb_aat_apply_context_t *c;
    136 
    137    accelerator_t (const KerxSubTableFormat0 &table_,
    138 	   hb_aat_apply_context_t *c_) :
    139 	     table (table_), c (c_) {}
    140 
    141    int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
    142    {
    143      if (!(*c->first_set)[left] || !(*c->second_set)[right]) return 0;
    144      return table.get_kerning (left, right, c);
    145    }
    146  };
    147 
    148 
    149  bool sanitize (hb_sanitize_context_t *c) const
    150  {
    151    TRACE_SANITIZE (this);
    152    return_trace (likely (pairs.sanitize (c)));
    153  }
    154 
    155  protected:
    156  KernSubTableHeader	header;
    157  BinSearchArrayOf<KernPair, typename KernSubTableHeader::Types::HBUINT>
    158 		pairs;	/* Sorted kern records. */
    159  public:
    160  DEFINE_SIZE_ARRAY (KernSubTableHeader::static_size + 16, pairs);
    161 };
    162 
    163 
    164 template <bool extended>
    165 struct Format1Entry;
    166 
    167 template <>
    168 struct Format1Entry<true>
    169 {
    170  enum Flags
    171  {
    172    Push		= 0x8000,	/* If set, push this glyph on the kerning stack. */
    173    DontAdvance		= 0x4000,	/* If set, don't advance to the next glyph
    174 				 * before going to the new state. */
    175    Reset		= 0x2000,	/* If set, reset the kerning data (clear the stack) */
    176    Reserved		= 0x1FFF,	/* Not used; set to 0. */
    177  };
    178 
    179  struct EntryData
    180  {
    181    HBUINT16	kernActionIndex;/* Index into the kerning value array. If
    182 			 * this index is 0xFFFF, then no kerning
    183 			 * is to be performed. */
    184    public:
    185    DEFINE_SIZE_STATIC (2);
    186  };
    187 
    188  static bool initiateAction (const Entry<EntryData> &entry)
    189  { return entry.flags & Push; }
    190 
    191  static bool performAction (const Entry<EntryData> &entry)
    192  { return entry.data.kernActionIndex != 0xFFFF; }
    193 
    194  static unsigned int kernActionIndex (const Entry<EntryData> &entry)
    195  { return entry.data.kernActionIndex; }
    196 };
    197 template <>
    198 struct Format1Entry<false>
    199 {
    200  enum Flags
    201  {
    202    Push		= 0x8000,	/* If set, push this glyph on the kerning stack. */
    203    DontAdvance		= 0x4000,	/* If set, don't advance to the next glyph
    204 				 * before going to the new state. */
    205    Offset		= 0x3FFF,	/* Byte offset from beginning of subtable to the
    206 				 * value table for the glyphs on the kerning stack. */
    207 
    208    Reset		= 0x0000,	/* Not supported? */
    209  };
    210 
    211  typedef void EntryData;
    212 
    213  static bool initiateAction (const Entry<EntryData> &entry)
    214  { return entry.flags & Push; }
    215 
    216  static bool performAction (const Entry<EntryData> &entry)
    217  { return entry.flags & Offset; }
    218 
    219  static unsigned int kernActionIndex (const Entry<EntryData> &entry)
    220  { return entry.flags & Offset; }
    221 };
    222 
    223 template <typename KernSubTableHeader>
    224 struct KerxSubTableFormat1
    225 {
    226  typedef typename KernSubTableHeader::Types Types;
    227  typedef typename Types::HBUINT HBUINT;
    228 
    229  typedef Format1Entry<Types::extended> Format1EntryT;
    230  typedef typename Format1EntryT::EntryData EntryData;
    231 
    232  enum Flags
    233  {
    234    DontAdvance	= Format1EntryT::DontAdvance,
    235  };
    236 
    237  bool is_action_initiable (const Entry<EntryData> &entry) const
    238  {
    239    return Format1EntryT::initiateAction (entry);
    240  }
    241  bool is_actionable (const Entry<EntryData> &entry) const
    242  {
    243    return Format1EntryT::performAction (entry);
    244  }
    245 
    246  struct driver_context_t
    247  {
    248    static constexpr bool in_place = true;
    249 
    250    driver_context_t (const KerxSubTableFormat1 *table_,
    251 	      hb_aat_apply_context_t *c_) :
    252 c (c_),
    253 table (table_),
    254 /* Apparently the offset kernAction is from the beginning of the state-machine,
    255  * similar to offsets in morx table, NOT from beginning of this table, like
    256  * other subtables in kerx.  Discovered via testing. */
    257 kernAction (&table->machine + table->kernAction),
    258 depth (0),
    259 crossStream (table->header.coverage & table->header.CrossStream) {}
    260 
    261    void transition (hb_buffer_t *buffer,
    262 	     StateTableDriver<Types, EntryData, Flags> *driver,
    263 	     const Entry<EntryData> &entry)
    264    {
    265      unsigned int flags = entry.flags;
    266 
    267      if (flags & Format1EntryT::Reset)
    268 depth = 0;
    269 
    270      if (flags & Format1EntryT::Push)
    271      {
    272 if (likely (depth < ARRAY_LENGTH (stack)))
    273   stack[depth++] = buffer->idx;
    274 else
    275   depth = 0; /* Probably not what CoreText does, but better? */
    276      }
    277 
    278      if (Format1EntryT::performAction (entry) && depth)
    279      {
    280 unsigned int tuple_count = hb_max (1u, table->header.tuple_count ());
    281 
    282 unsigned int kern_idx = Format1EntryT::kernActionIndex (entry);
    283 kern_idx = Types::byteOffsetToIndex (kern_idx, &table->machine, kernAction.arrayZ);
    284 const FWORD *actions = &kernAction[kern_idx];
    285 if (!c->sanitizer.check_array (actions, depth, tuple_count))
    286 {
    287   depth = 0;
    288   return;
    289 }
    290 hb_barrier ();
    291 
    292 hb_mask_t kern_mask = c->plan->kern_mask;
    293 
    294 /* From Apple 'kern' spec:
    295  * "Each pops one glyph from the kerning stack and applies the kerning value to it.
    296  * The end of the list is marked by an odd value... */
    297 bool last = false;
    298 while (!last && depth)
    299 {
    300   unsigned int idx = stack[--depth];
    301   int v = *actions;
    302   actions += tuple_count;
    303   if (idx >= buffer->len) continue;
    304 
    305   /* "The end of the list is marked by an odd value..." */
    306   last = v & 1;
    307   v &= ~1;
    308 
    309   hb_glyph_position_t &o = buffer->pos[idx];
    310 
    311   if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
    312   {
    313     if (crossStream)
    314     {
    315       /* The following flag is undocumented in the spec, but described
    316        * in the 'kern' table example. */
    317       if (v == -0x8000)
    318       {
    319 	o.attach_type() = OT::Layout::GPOS_impl::ATTACH_TYPE_NONE;
    320 	o.attach_chain() = 0;
    321 	o.y_offset = 0;
    322       }
    323       else if (o.attach_type())
    324       {
    325 	o.y_offset += c->font->em_scale_y (v);
    326 	buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
    327       }
    328     }
    329     else if (buffer->info[idx].mask & kern_mask)
    330     {
    331       auto scaled = c->font->em_scale_x (v);
    332       o.x_advance += scaled;
    333       o.x_offset += scaled;
    334     }
    335   }
    336   else
    337   {
    338     if (crossStream)
    339     {
    340       /* CoreText doesn't do crossStream kerning in vertical.  We do. */
    341       if (v == -0x8000)
    342       {
    343 	o.attach_type() = OT::Layout::GPOS_impl::ATTACH_TYPE_NONE;
    344 	o.attach_chain() = 0;
    345 	o.x_offset = 0;
    346       }
    347       else if (o.attach_type())
    348       {
    349 	o.x_offset += c->font->em_scale_x (v);
    350 	buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
    351       }
    352     }
    353     else if (buffer->info[idx].mask & kern_mask)
    354     {
    355       o.y_advance += c->font->em_scale_y (v);
    356       o.y_offset += c->font->em_scale_y (v);
    357     }
    358   }
    359 }
    360      }
    361    }
    362 
    363    public:
    364    hb_aat_apply_context_t *c;
    365    const KerxSubTableFormat1 *table;
    366    private:
    367    const UnsizedArrayOf<FWORD> &kernAction;
    368    unsigned int stack[8];
    369    unsigned int depth;
    370    bool crossStream;
    371  };
    372 
    373  bool apply (hb_aat_apply_context_t *c) const
    374  {
    375    TRACE_APPLY (this);
    376 
    377    if (!c->plan->requested_kerning &&
    378 !(header.coverage & header.CrossStream))
    379      return false;
    380 
    381    driver_context_t dc (this, c);
    382 
    383    StateTableDriver<Types, EntryData, Flags> driver (machine, c->font->face);
    384 
    385    driver.drive (&dc, c);
    386 
    387    return_trace (true);
    388  }
    389 
    390  bool sanitize (hb_sanitize_context_t *c) const
    391  {
    392    TRACE_SANITIZE (this);
    393    /* The rest of array sanitizations are done at run-time. */
    394    return_trace (likely (c->check_struct (this) &&
    395 		  machine.sanitize (c)));
    396  }
    397 
    398  template <typename set_t>
    399  void collect_glyphs (set_t &first_set, set_t &second_set, unsigned num_glyphs) const
    400  {
    401    machine.collect_initial_glyphs (first_set, num_glyphs, *this);
    402    //machine.collect_glyphs (second_set, num_glyphs); // second_set is unused for machine kerning
    403  }
    404 
    405  protected:
    406  KernSubTableHeader				header;
    407  StateTable<Types, EntryData>			machine;
    408  NNOffsetTo<UnsizedArrayOf<FWORD>, HBUINT>	kernAction;
    409  public:
    410  DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + (StateTable<Types, EntryData>::static_size + HBUINT::static_size));
    411 };
    412 
    413 template <typename KernSubTableHeader>
    414 struct KerxSubTableFormat2
    415 {
    416  typedef typename KernSubTableHeader::Types Types;
    417  typedef typename Types::HBUINT HBUINT;
    418 
    419  int get_kerning (hb_codepoint_t left, hb_codepoint_t right,
    420 	   hb_aat_apply_context_t *c) const
    421  {
    422    unsigned int num_glyphs = c->sanitizer.get_num_glyphs ();
    423    unsigned int l = (this+leftClassTable).get_class (left, num_glyphs, 0);
    424    unsigned int r = (this+rightClassTable).get_class (right, num_glyphs, 0);
    425 
    426    const UnsizedArrayOf<FWORD> &arrayZ = this+array;
    427    unsigned int kern_idx = l + r;
    428    kern_idx = Types::offsetToIndex (kern_idx, this, arrayZ.arrayZ);
    429    const FWORD *v = &arrayZ[kern_idx];
    430    if (unlikely (!v->sanitize (&c->sanitizer))) return 0;
    431    hb_barrier ();
    432 
    433    return kerxTupleKern (*v, header.tuple_count (), this, c);
    434  }
    435 
    436  bool apply (hb_aat_apply_context_t *c) const
    437  {
    438    TRACE_APPLY (this);
    439 
    440    if (!c->plan->requested_kerning)
    441      return_trace (false);
    442 
    443    if (header.coverage & header.Backwards)
    444      return_trace (false);
    445 
    446    accelerator_t accel (*this, c);
    447    hb_kern_machine_t<accelerator_t> machine (accel, header.coverage & header.CrossStream);
    448    machine.kern (c->font, c->buffer, c->plan->kern_mask);
    449 
    450    return_trace (true);
    451  }
    452 
    453  template <typename set_t>
    454  void collect_glyphs (set_t &first_set, set_t &second_set, unsigned num_glyphs) const
    455  {
    456    (this+leftClassTable).collect_glyphs (first_set, num_glyphs);
    457    (this+rightClassTable).collect_glyphs (second_set, num_glyphs);
    458  }
    459 
    460  struct accelerator_t
    461  {
    462    const KerxSubTableFormat2 &table;
    463    hb_aat_apply_context_t *c;
    464 
    465    accelerator_t (const KerxSubTableFormat2 &table_,
    466 	   hb_aat_apply_context_t *c_) :
    467 	     table (table_), c (c_) {}
    468 
    469    int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
    470    {
    471      if (!(*c->first_set)[left] || !(*c->second_set)[right]) return 0;
    472      return table.get_kerning (left, right, c);
    473    }
    474  };
    475 
    476  bool sanitize (hb_sanitize_context_t *c) const
    477  {
    478    TRACE_SANITIZE (this);
    479    return_trace (likely (c->check_struct (this) &&
    480 		  leftClassTable.sanitize (c, this) &&
    481 		  rightClassTable.sanitize (c, this) &&
    482 		  hb_barrier () &&
    483 		  c->check_range (this, array)));
    484  }
    485 
    486  protected:
    487  KernSubTableHeader	header;
    488  HBUINT		rowWidth;	/* The width, in bytes, of a row in the table. */
    489  NNOffsetTo<typename Types::ClassTypeWide, HBUINT>
    490 		leftClassTable;	/* Offset from beginning of this subtable to
    491 				 * left-hand class table. */
    492  NNOffsetTo<typename Types::ClassTypeWide, HBUINT>
    493 		rightClassTable;/* Offset from beginning of this subtable to
    494 				 * right-hand class table. */
    495  NNOffsetTo<UnsizedArrayOf<FWORD>, HBUINT>
    496 		 array;		/* Offset from beginning of this subtable to
    497 				 * the start of the kerning array. */
    498  public:
    499  DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 4 * sizeof (HBUINT));
    500 };
    501 
    502 template <typename KernSubTableHeader>
    503 struct KerxSubTableFormat4
    504 {
    505  typedef ExtendedTypes Types;
    506 
    507  struct EntryData
    508  {
    509    HBUINT16	ankrActionIndex;/* Either 0xFFFF (for no action) or the index of
    510 			 * the action to perform. */
    511    public:
    512    DEFINE_SIZE_STATIC (2);
    513  };
    514 
    515  enum Flags
    516  {
    517    Mark		= 0x8000,	/* If set, remember this glyph as the marked glyph. */
    518    DontAdvance		= 0x4000,	/* If set, don't advance to the next glyph before
    519 				 * going to the new state. */
    520    Reserved		= 0x3FFF,	/* Not used; set to 0. */
    521  };
    522 
    523  bool is_action_initiable (const Entry<EntryData> &entry) const
    524  {
    525    return (entry.flags & Mark);
    526  }
    527  bool is_actionable (const Entry<EntryData> &entry) const
    528  {
    529    return entry.data.ankrActionIndex != 0xFFFF;
    530  }
    531 
    532  struct driver_context_t
    533  {
    534    static constexpr bool in_place = true;
    535    enum SubTableFlags
    536    {
    537      ActionType	= 0xC0000000,	/* A two-bit field containing the action type. */
    538      Unused		= 0x3F000000,	/* Unused - must be zero. */
    539      Offset		= 0x00FFFFFF,	/* Masks the offset in bytes from the beginning
    540 				 * of the subtable to the beginning of the control
    541 				 * point table. */
    542    };
    543 
    544    driver_context_t (const KerxSubTableFormat4 *table_,
    545 	      hb_aat_apply_context_t *c_) :
    546 c (c_),
    547 table (table_),
    548 action_type ((table->flags & ActionType) >> 30),
    549 ankrData ((HBUINT16 *) ((const char *) &table->machine + (table->flags & Offset))),
    550 mark_set (false),
    551 mark (0) {}
    552 
    553    void transition (hb_buffer_t *buffer,
    554 	     StateTableDriver<Types, EntryData, Flags> *driver,
    555 	     const Entry<EntryData> &entry)
    556    {
    557      if (mark_set && entry.data.ankrActionIndex != 0xFFFF && buffer->idx < buffer->len)
    558      {
    559 hb_glyph_position_t &o = buffer->cur_pos();
    560 switch (action_type)
    561 {
    562   case 0: /* Control Point Actions.*/
    563   {
    564     /* Indexed into glyph outline. */
    565     /* Each action (record in ankrData) contains two 16-bit fields, so we must
    566        double the ankrActionIndex to get the correct offset here. */
    567     const HBUINT16 *data = &ankrData[entry.data.ankrActionIndex * 2];
    568     if (!c->sanitizer.check_array (data, 2)) return;
    569     hb_barrier ();
    570     unsigned int markControlPoint = *data++;
    571     unsigned int currControlPoint = *data++;
    572     hb_position_t markX = 0;
    573     hb_position_t markY = 0;
    574     hb_position_t currX = 0;
    575     hb_position_t currY = 0;
    576     if (!c->font->get_glyph_contour_point_for_origin (c->buffer->info[mark].codepoint,
    577 						      markControlPoint,
    578 						      HB_DIRECTION_LTR /*XXX*/,
    579 						      &markX, &markY) ||
    580 	!c->font->get_glyph_contour_point_for_origin (c->buffer->cur ().codepoint,
    581 						      currControlPoint,
    582 						      HB_DIRECTION_LTR /*XXX*/,
    583 						      &currX, &currY))
    584       return;
    585 
    586     o.x_offset = markX - currX;
    587     o.y_offset = markY - currY;
    588   }
    589   break;
    590 
    591   case 1: /* Anchor Point Actions. */
    592   {
    593     /* Indexed into 'ankr' table. */
    594     /* Each action (record in ankrData) contains two 16-bit fields, so we must
    595        double the ankrActionIndex to get the correct offset here. */
    596     const HBUINT16 *data = &ankrData[entry.data.ankrActionIndex * 2];
    597     if (!c->sanitizer.check_array (data, 2)) return;
    598     hb_barrier ();
    599     unsigned int markAnchorPoint = *data++;
    600     unsigned int currAnchorPoint = *data++;
    601     const Anchor &markAnchor = c->ankr_table->get_anchor (c->buffer->info[mark].codepoint,
    602 							  markAnchorPoint,
    603 							  c->sanitizer.get_num_glyphs ());
    604     const Anchor &currAnchor = c->ankr_table->get_anchor (c->buffer->cur ().codepoint,
    605 							  currAnchorPoint,
    606 							  c->sanitizer.get_num_glyphs ());
    607 
    608     o.x_offset = c->font->em_scale_x (markAnchor.xCoordinate) - c->font->em_scale_x (currAnchor.xCoordinate);
    609     o.y_offset = c->font->em_scale_y (markAnchor.yCoordinate) - c->font->em_scale_y (currAnchor.yCoordinate);
    610   }
    611   break;
    612 
    613   case 2: /* Control Point Coordinate Actions. */
    614   {
    615     /* Each action contains four 16-bit fields, so we multiply the ankrActionIndex
    616        by 4 to get the correct offset for the given action. */
    617     const FWORD *data = (const FWORD *) &ankrData[entry.data.ankrActionIndex * 4];
    618     if (!c->sanitizer.check_array (data, 4)) return;
    619     hb_barrier ();
    620     int markX = *data++;
    621     int markY = *data++;
    622     int currX = *data++;
    623     int currY = *data++;
    624 
    625     o.x_offset = c->font->em_scale_x (markX) - c->font->em_scale_x (currX);
    626     o.y_offset = c->font->em_scale_y (markY) - c->font->em_scale_y (currY);
    627   }
    628   break;
    629 }
    630 o.attach_type() = OT::Layout::GPOS_impl::ATTACH_TYPE_MARK;
    631 o.attach_chain() = (int) mark - (int) buffer->idx;
    632 if (c->buffer_is_reversed)
    633   o.attach_chain() = -o.attach_chain();
    634 buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
    635      }
    636 
    637      if (entry.flags & Mark)
    638      {
    639 mark_set = true;
    640 mark = buffer->idx;
    641      }
    642    }
    643 
    644    public:
    645    hb_aat_apply_context_t *c;
    646    const KerxSubTableFormat4 *table;
    647    private:
    648    unsigned int action_type;
    649    const HBUINT16 *ankrData;
    650    bool mark_set;
    651    unsigned int mark;
    652  };
    653 
    654  bool apply (hb_aat_apply_context_t *c) const
    655  {
    656    TRACE_APPLY (this);
    657 
    658    driver_context_t dc (this, c);
    659 
    660    StateTableDriver<Types, EntryData, Flags> driver (machine, c->font->face);
    661 
    662    driver.drive (&dc, c);
    663 
    664    return_trace (true);
    665  }
    666 
    667  bool sanitize (hb_sanitize_context_t *c) const
    668  {
    669    TRACE_SANITIZE (this);
    670    /* The rest of array sanitizations are done at run-time. */
    671    return_trace (likely (c->check_struct (this) &&
    672 		  machine.sanitize (c)));
    673  }
    674 
    675  template <typename set_t>
    676  void collect_glyphs (set_t &first_set, set_t &second_set, unsigned num_glyphs) const
    677  {
    678    machine.collect_initial_glyphs (first_set, num_glyphs, *this);
    679    //machine.collect_glyphs (second_set, num_glyphs); // second_set is unused for machine kerning
    680  }
    681 
    682  protected:
    683  KernSubTableHeader		header;
    684  StateTable<Types, EntryData>	machine;
    685  HBUINT32			flags;
    686  public:
    687  DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + (StateTable<Types, EntryData>::static_size + HBUINT32::static_size));
    688 };
    689 
    690 template <typename KernSubTableHeader>
    691 struct KerxSubTableFormat6
    692 {
    693  enum Flags
    694  {
    695    ValuesAreLong	= 0x00000001,
    696  };
    697 
    698  bool is_long () const { return flags & ValuesAreLong; }
    699 
    700  int get_kerning (hb_codepoint_t left, hb_codepoint_t right,
    701 	   hb_aat_apply_context_t *c) const
    702  {
    703    unsigned int num_glyphs = c->sanitizer.get_num_glyphs ();
    704    if (is_long ())
    705    {
    706      const auto &t = u.l;
    707      unsigned int l = (this+t.rowIndexTable).get_value_or_null (left, num_glyphs);
    708      unsigned int r = (this+t.columnIndexTable).get_value_or_null (right, num_glyphs);
    709      unsigned int offset = l + r;
    710      if (unlikely (offset < l)) return 0; /* Addition overflow. */
    711      if (unlikely (hb_unsigned_mul_overflows (offset, sizeof (FWORD32)))) return 0;
    712      const FWORD32 *v = &StructAtOffset<FWORD32> (&(this+t.array), offset * sizeof (FWORD32));
    713      if (unlikely (!v->sanitize (&c->sanitizer))) return 0;
    714      hb_barrier ();
    715      return kerxTupleKern (*v, header.tuple_count (), &(this+vector), c);
    716    }
    717    else
    718    {
    719      const auto &t = u.s;
    720      unsigned int l = (this+t.rowIndexTable).get_value_or_null (left, num_glyphs);
    721      unsigned int r = (this+t.columnIndexTable).get_value_or_null (right, num_glyphs);
    722      unsigned int offset = l + r;
    723      const FWORD *v = &StructAtOffset<FWORD> (&(this+t.array), offset * sizeof (FWORD));
    724      if (unlikely (!v->sanitize (&c->sanitizer))) return 0;
    725      hb_barrier ();
    726      return kerxTupleKern (*v, header.tuple_count (), &(this+vector), c);
    727    }
    728  }
    729 
    730  bool apply (hb_aat_apply_context_t *c) const
    731  {
    732    TRACE_APPLY (this);
    733 
    734    if (!c->plan->requested_kerning)
    735      return_trace (false);
    736 
    737    if (header.coverage & header.Backwards)
    738      return_trace (false);
    739 
    740    accelerator_t accel (*this, c);
    741    hb_kern_machine_t<accelerator_t> machine (accel, header.coverage & header.CrossStream);
    742    machine.kern (c->font, c->buffer, c->plan->kern_mask);
    743 
    744    return_trace (true);
    745  }
    746 
    747  bool sanitize (hb_sanitize_context_t *c) const
    748  {
    749    TRACE_SANITIZE (this);
    750    return_trace (likely (c->check_struct (this) &&
    751 		  hb_barrier () &&
    752 		  (is_long () ?
    753 		   (
    754 		     u.l.rowIndexTable.sanitize (c, this) &&
    755 		     u.l.columnIndexTable.sanitize (c, this) &&
    756 		     c->check_range (this, u.l.array)
    757 		   ) : (
    758 		     u.s.rowIndexTable.sanitize (c, this) &&
    759 		     u.s.columnIndexTable.sanitize (c, this) &&
    760 		     c->check_range (this, u.s.array)
    761 		   )) &&
    762 		  (header.tuple_count () == 0 ||
    763 		   c->check_range (this, vector))));
    764  }
    765 
    766  template <typename set_t>
    767  void collect_glyphs (set_t &first_set, set_t &second_set, unsigned num_glyphs) const
    768  {
    769    if (is_long ())
    770    {
    771      const auto &t = u.l;
    772      (this+t.rowIndexTable).collect_glyphs (first_set, num_glyphs);
    773      (this+t.columnIndexTable).collect_glyphs (second_set, num_glyphs);
    774    }
    775    else
    776    {
    777      const auto &t = u.s;
    778      (this+t.rowIndexTable).collect_glyphs (first_set, num_glyphs);
    779      (this+t.columnIndexTable).collect_glyphs (second_set, num_glyphs);
    780    }
    781  }
    782 
    783  struct accelerator_t
    784  {
    785    const KerxSubTableFormat6 &table;
    786    hb_aat_apply_context_t *c;
    787 
    788    accelerator_t (const KerxSubTableFormat6 &table_,
    789 	   hb_aat_apply_context_t *c_) :
    790 	     table (table_), c (c_) {}
    791 
    792    int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
    793    {
    794      if (!(*c->first_set)[left] || !(*c->second_set)[right]) return 0;
    795      return table.get_kerning (left, right, c);
    796    }
    797  };
    798 
    799  protected:
    800  KernSubTableHeader		header;
    801  HBUINT32			flags;
    802  HBUINT16			rowCount;
    803  HBUINT16			columnCount;
    804  union U
    805  {
    806    struct Long
    807    {
    808      NNOffset32To<Lookup<HBUINT32>>		rowIndexTable;
    809      NNOffset32To<Lookup<HBUINT32>>		columnIndexTable;
    810      NNOffset32To<UnsizedArrayOf<FWORD32>>	array;
    811    } l;
    812    struct Short
    813    {
    814      NNOffset32To<Lookup<HBUINT16>>		rowIndexTable;
    815      NNOffset32To<Lookup<HBUINT16>>		columnIndexTable;
    816      NNOffset32To<UnsizedArrayOf<FWORD>>	array;
    817    } s;
    818  } u;
    819  NNOffset32To<UnsizedArrayOf<FWORD>>	vector;
    820  public:
    821  DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 24);
    822 };
    823 
    824 
    825 struct KerxSubTableHeader
    826 {
    827  typedef ExtendedTypes Types;
    828 
    829  unsigned   tuple_count () const { return tupleCount; }
    830  bool     is_horizontal () const { return !(coverage & Vertical); }
    831 
    832  enum Coverage
    833  {
    834    Vertical	= 0x80000000u,	/* Set if table has vertical kerning values. */
    835    CrossStream	= 0x40000000u,	/* Set if table has cross-stream kerning values. */
    836    Variation	= 0x20000000u,	/* Set if table has variation kerning values. */
    837    Backwards	= 0x10000000u,	/* If clear, process the glyphs forwards, that
    838 			 * is, from first to last in the glyph stream.
    839 			 * If we, process them from last to first.
    840 			 * This flag only applies to state-table based
    841 			 * 'kerx' subtables (types 1 and 4). */
    842    Reserved	= 0x0FFFFF00u,	/* Reserved, set to zero. */
    843    SubtableType= 0x000000FFu,	/* Subtable type. */
    844  };
    845 
    846  bool sanitize (hb_sanitize_context_t *c) const
    847  {
    848    TRACE_SANITIZE (this);
    849    return_trace (c->check_struct (this));
    850  }
    851 
    852  public:
    853  HBUINT32	length;
    854  HBUINT32	coverage;
    855  HBUINT32	tupleCount;
    856  public:
    857  DEFINE_SIZE_STATIC (12);
    858 };
    859 
    860 struct KerxSubTable
    861 {
    862  friend struct kerx;
    863 
    864  unsigned int get_size () const { return u.header.length; }
    865  unsigned int get_type () const { return u.header.coverage & u.header.SubtableType; }
    866 
    867  template <typename context_t, typename ...Ts>
    868  typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
    869  {
    870    unsigned int subtable_type = get_type ();
    871    TRACE_DISPATCH (this, subtable_type);
    872    switch (subtable_type) {
    873    case 0:	return_trace (c->dispatch (u.format0, std::forward<Ts> (ds)...));
    874    case 1:	return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
    875    case 2:	return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...));
    876    case 4:	return_trace (c->dispatch (u.format4, std::forward<Ts> (ds)...));
    877    case 6:	return_trace (c->dispatch (u.format6, std::forward<Ts> (ds)...));
    878    default:	return_trace (c->default_return_value ());
    879    }
    880  }
    881 
    882  template <typename set_t>
    883  void collect_glyphs (set_t &first_set, set_t &second_set, unsigned num_glyphs) const
    884  {
    885    unsigned int subtable_type = get_type ();
    886    switch (subtable_type) {
    887    case 0:	u.format0.collect_glyphs (first_set, second_set, num_glyphs); return;
    888    case 1:	u.format1.collect_glyphs (first_set, second_set, num_glyphs); return;
    889    case 2:	u.format2.collect_glyphs (first_set, second_set, num_glyphs); return;
    890    case 4:	u.format4.collect_glyphs (first_set, second_set, num_glyphs); return;
    891    case 6:	u.format6.collect_glyphs (first_set, second_set, num_glyphs); return;
    892    default:	return;
    893    }
    894  }
    895 
    896  bool sanitize (hb_sanitize_context_t *c) const
    897  {
    898    TRACE_SANITIZE (this);
    899    if (!(u.header.sanitize (c) &&
    900   hb_barrier () &&
    901   u.header.length >= u.header.static_size &&
    902   c->check_range (this, u.header.length)))
    903      return_trace (false);
    904 
    905    return_trace (dispatch (c));
    906  }
    907 
    908  public:
    909  union {
    910  KerxSubTableHeader				header;
    911  KerxSubTableFormat0<KerxSubTableHeader>	format0;
    912  KerxSubTableFormat1<KerxSubTableHeader>	format1;
    913  KerxSubTableFormat2<KerxSubTableHeader>	format2;
    914  KerxSubTableFormat4<KerxSubTableHeader>	format4;
    915  KerxSubTableFormat6<KerxSubTableHeader>	format6;
    916  } u;
    917  public:
    918  DEFINE_SIZE_MIN (12);
    919 };
    920 
    921 
    922 /*
    923 * The 'kerx' Table
    924 */
    925 
    926 struct kern_subtable_accelerator_data_t
    927 {
    928  hb_bit_set_t first_set;
    929  hb_bit_set_t second_set;
    930  mutable hb_aat_class_cache_t class_cache;
    931 };
    932 
    933 struct kern_accelerator_data_t
    934 {
    935  hb_vector_t<kern_subtable_accelerator_data_t> subtable_accels;
    936  hb_aat_scratch_t scratch;
    937 };
    938 
    939 template <typename T>
    940 struct KerxTable
    941 {
    942  /* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */
    943  const T* thiz () const { return static_cast<const T *> (this); }
    944 
    945  bool has_state_machine () const
    946  {
    947    typedef typename T::SubTable SubTable;
    948 
    949    const SubTable *st = &thiz()->firstSubTable;
    950    unsigned int count = thiz()->tableCount;
    951    for (unsigned int i = 0; i < count; i++)
    952    {
    953      if (st->get_type () == 1)
    954 return true;
    955 
    956      // TODO: What about format 4? What's this API used for anyway?
    957 
    958      st = &StructAfter<SubTable> (*st);
    959    }
    960    return false;
    961  }
    962 
    963  bool has_cross_stream () const
    964  {
    965    typedef typename T::SubTable SubTable;
    966 
    967    const SubTable *st = &thiz()->firstSubTable;
    968    unsigned int count = thiz()->tableCount;
    969    for (unsigned int i = 0; i < count; i++)
    970    {
    971      if (st->u.header.coverage & st->u.header.CrossStream)
    972 return true;
    973      st = &StructAfter<SubTable> (*st);
    974    }
    975    return false;
    976  }
    977 
    978  int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right) const
    979  {
    980    typedef typename T::SubTable SubTable;
    981 
    982    int v = 0;
    983    const SubTable *st = &thiz()->firstSubTable;
    984    unsigned int count = thiz()->tableCount;
    985    for (unsigned int i = 0; i < count; i++)
    986    {
    987      if ((st->u.header.coverage & (st->u.header.Variation | st->u.header.CrossStream)) ||
    988   !st->u.header.is_horizontal ())
    989 continue;
    990      v += st->get_kerning (left, right);
    991      st = &StructAfter<SubTable> (*st);
    992    }
    993    return v;
    994  }
    995 
    996  bool apply (AAT::hb_aat_apply_context_t *c,
    997       const kern_accelerator_data_t &accel_data) const
    998  {
    999    c->buffer->unsafe_to_concat ();
   1000 
   1001    c->setup_buffer_glyph_set ();
   1002 
   1003    typedef typename T::SubTable SubTable;
   1004 
   1005    bool ret = false;
   1006    bool seenCrossStream = false;
   1007    c->set_lookup_index (0);
   1008    const SubTable *st = &thiz()->firstSubTable;
   1009    unsigned int count = thiz()->tableCount;
   1010    for (unsigned int i = 0; i < count; i++)
   1011    {
   1012      bool reverse;
   1013 
   1014      auto &subtable_accel = accel_data.subtable_accels[i];
   1015 
   1016      if (!T::Types::extended && (st->u.header.coverage & st->u.header.Variation))
   1017 goto skip;
   1018 
   1019      if (HB_DIRECTION_IS_HORIZONTAL (c->buffer->props.direction) != st->u.header.is_horizontal ())
   1020 goto skip;
   1021 
   1022      c->first_set = &subtable_accel.first_set;
   1023      c->second_set = &subtable_accel.second_set;
   1024      c->machine_class_cache = &subtable_accel.class_cache;
   1025 
   1026      if (!c->buffer_intersects_machine ())
   1027      {
   1028 (void) c->buffer->message (c->font, "skipped subtable %u because no glyph matches", c->lookup_index);
   1029 goto skip;
   1030      }
   1031 
   1032      reverse = bool (st->u.header.coverage & st->u.header.Backwards) !=
   1033 	HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction);
   1034 
   1035      if (!c->buffer->message (c->font, "start subtable %u", c->lookup_index))
   1036 goto skip;
   1037 
   1038      if (!seenCrossStream &&
   1039   (st->u.header.coverage & st->u.header.CrossStream))
   1040      {
   1041 /* Attach all glyphs into a chain. */
   1042 seenCrossStream = true;
   1043 hb_glyph_position_t *pos = c->buffer->pos;
   1044 unsigned int count = c->buffer->len;
   1045 for (unsigned int i = 0; i < count; i++)
   1046 {
   1047   pos[i].attach_type() = OT::Layout::GPOS_impl::ATTACH_TYPE_CURSIVE;
   1048   pos[i].attach_chain() = HB_DIRECTION_IS_FORWARD (c->buffer->props.direction) ? -1 : +1;
   1049   /* We intentionally don't set HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT,
   1050    * since there needs to be a non-zero attachment for post-positioning to
   1051    * be needed. */
   1052 }
   1053      }
   1054 
   1055      if (reverse != c->buffer_is_reversed)
   1056        c->reverse_buffer ();
   1057 
   1058      {
   1059 /* See comment in sanitize() for conditional here. */
   1060 hb_sanitize_with_object_t with (&c->sanitizer, i < count - 1 ? st : (const SubTable *) nullptr);
   1061 ret |= st->dispatch (c);
   1062      }
   1063 
   1064      (void) c->buffer->message (c->font, "end subtable %u", c->lookup_index);
   1065 
   1066    skip:
   1067      st = &StructAfter<SubTable> (*st);
   1068      c->set_lookup_index (c->lookup_index + 1);
   1069    }
   1070    if (c->buffer_is_reversed)
   1071      c->reverse_buffer ();
   1072 
   1073    return ret;
   1074  }
   1075 
   1076  bool sanitize (hb_sanitize_context_t *c) const
   1077  {
   1078    TRACE_SANITIZE (this);
   1079    if (unlikely (!(thiz()->version.sanitize (c) &&
   1080 	    hb_barrier () &&
   1081 	    (unsigned) thiz()->version >= (unsigned) T::minVersion &&
   1082 	    thiz()->tableCount.sanitize (c))))
   1083      return_trace (false);
   1084 
   1085    typedef typename T::SubTable SubTable;
   1086 
   1087    const SubTable *st = &thiz()->firstSubTable;
   1088    unsigned int count = thiz()->tableCount;
   1089    for (unsigned int i = 0; i < count; i++)
   1090    {
   1091      if (unlikely (!st->u.header.sanitize (c)))
   1092 return_trace (false);
   1093      hb_barrier ();
   1094      /* OpenType kern table has 2-byte subtable lengths.  That's limiting.
   1095       * MS implementation also only supports one subtable, of format 0,
   1096       * anyway.  Certain versions of some fonts, like Calibry, contain
   1097       * kern subtable that exceeds 64kb.  Looks like, the subtable length
   1098       * is simply ignored.  Which makes sense.  It's only needed if you
   1099       * have multiple subtables.  To handle such fonts, we just ignore
   1100       * the length for the last subtable. */
   1101      hb_sanitize_with_object_t with (c, i < count - 1 ? st : (const SubTable *) nullptr);
   1102 
   1103      if (unlikely (!st->sanitize (c)))
   1104 return_trace (false);
   1105 
   1106      st = &StructAfter<SubTable> (*st);
   1107    }
   1108 
   1109    unsigned majorVersion = thiz()->version;
   1110    if (sizeof (thiz()->version) == 4)
   1111      majorVersion = majorVersion >> 16;
   1112    if (majorVersion >= 3)
   1113    {
   1114      const SubtableGlyphCoverage *coverage = (const SubtableGlyphCoverage *) st;
   1115      if (!coverage->sanitize (c, count))
   1116        return_trace (false);
   1117    }
   1118 
   1119    return_trace (true);
   1120  }
   1121 
   1122  kern_accelerator_data_t create_accelerator_data (unsigned num_glyphs) const
   1123  {
   1124    kern_accelerator_data_t accel_data;
   1125 
   1126    typedef typename T::SubTable SubTable;
   1127 
   1128    const SubTable *st = &thiz()->firstSubTable;
   1129    unsigned int count = thiz()->tableCount;
   1130    for (unsigned int i = 0; i < count; i++)
   1131    {
   1132      auto &subtable_accel = *accel_data.subtable_accels.push ();
   1133      if (unlikely (accel_data.subtable_accels.in_error ()))
   1134   return accel_data;
   1135 
   1136      st->collect_glyphs (subtable_accel.first_set, subtable_accel.second_set, num_glyphs);
   1137      subtable_accel.class_cache.clear ();
   1138 
   1139      st = &StructAfter<SubTable> (*st);
   1140    }
   1141 
   1142    return accel_data;
   1143  }
   1144 
   1145  struct accelerator_t
   1146  {
   1147    accelerator_t (hb_face_t *face)
   1148    {
   1149      hb_sanitize_context_t sc;
   1150      this->table = sc.reference_table<T> (face);
   1151      this->accel_data = this->table->create_accelerator_data (face->get_num_glyphs ());
   1152    }
   1153    ~accelerator_t ()
   1154    {
   1155      this->table.destroy ();
   1156    }
   1157 
   1158    hb_blob_t *get_blob () const { return table.get_blob (); }
   1159 
   1160    bool apply (AAT::hb_aat_apply_context_t *c) const
   1161    {
   1162      return table->apply (c, accel_data);
   1163    }
   1164 
   1165    hb_blob_ptr_t<T> table;
   1166    kern_accelerator_data_t accel_data;
   1167    hb_aat_scratch_t scratch;
   1168  };
   1169 };
   1170 
   1171 struct kerx : KerxTable<kerx>
   1172 {
   1173  friend struct KerxTable<kerx>;
   1174 
   1175  static constexpr hb_tag_t tableTag = HB_AAT_TAG_kerx;
   1176  static constexpr unsigned minVersion = 2u;
   1177 
   1178  typedef KerxSubTableHeader SubTableHeader;
   1179  typedef SubTableHeader::Types Types;
   1180  typedef KerxSubTable SubTable;
   1181 
   1182  bool has_data () const { return version; }
   1183 
   1184  protected:
   1185  HBUINT16	version;	/* The version number of the extended kerning table
   1186 			 * (currently 2, 3, or 4). */
   1187  HBUINT16	unused;		/* Set to 0. */
   1188  HBUINT32	tableCount;	/* The number of subtables included in the extended kerning
   1189 			 * table. */
   1190  SubTable	firstSubTable;	/* Subtables. */
   1191 /*subtableGlyphCoverageArray*/	/* Only if version >= 3. We don't use. */
   1192 
   1193  public:
   1194  DEFINE_SIZE_MIN (8);
   1195 };
   1196 
   1197 struct kerx_accelerator_t : kerx::accelerator_t {
   1198  kerx_accelerator_t (hb_face_t *face) : kerx::accelerator_t (face) {}
   1199 };
   1200 
   1201 } /* namespace AAT */
   1202 
   1203 #endif /* HB_AAT_LAYOUT_KERX_TABLE_HH */