[HarfBuzz] harfbuzz: Branch 'master' - 68 commits

Behdad Esfahbod behdad at kemper.freedesktop.org
Thu Nov 8 02:29:33 UTC 2018


 src/Makefile.sources            |    1 
 src/hb-aat-layout-common.hh     |  127 +++++--
 src/hb-aat-layout-kerx-table.hh |  508 ++++++++++++++++++++++---------
 src/hb-aat-layout-morx-table.hh |   71 +---
 src/hb-aat-layout.cc            |   30 +
 src/hb-aat-layout.hh            |   12 
 src/hb-kern.hh                  |  139 ++++++++
 src/hb-ot-kern-table.hh         |  654 ++++++----------------------------------
 src/hb-ot-layout-gpos-table.hh  |   60 ++-
 src/hb-ot-layout.cc             |   70 +++-
 src/hb-ot-layout.hh             |   24 -
 src/hb-ot-shape-fallback.cc     |   13 
 src/hb-ot-shape.cc              |  134 ++------
 src/hb-ot-shape.hh              |    2 
 test/fuzzing/hb-shape-fuzzer.cc |   24 -
 15 files changed, 957 insertions(+), 912 deletions(-)

New commits:
commit 0bf76154f1bb15aa2fc361eb725977313f103a58
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 19:11:43 2018 -0500

    [fuzzing] Take whatever text we can

diff --git a/test/fuzzing/hb-shape-fuzzer.cc b/test/fuzzing/hb-shape-fuzzer.cc
index 70ad08b4..5723db90 100644
--- a/test/fuzzing/hb-shape-fuzzer.cc
+++ b/test/fuzzing/hb-shape-fuzzer.cc
@@ -25,18 +25,20 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
     hb_buffer_destroy (buffer);
   }
 
-  uint32_t text32[16];
-  if (size > sizeof (text32)) {
-    memcpy(text32, data + size - sizeof (text32), sizeof (text32));
-    hb_buffer_t *buffer = hb_buffer_create ();
-    hb_buffer_add_utf32 (buffer, text32, sizeof (text32) / sizeof (text32[0]), 0, -1);
-    hb_buffer_guess_segment_properties (buffer);
-    hb_shape (font, buffer, NULL, 0);
-    hb_buffer_destroy (buffer);
+  uint32_t text32[16] = {0};
+  unsigned int len = sizeof (text32);
+  if (size < len)
+    len = size;
+  memcpy(text32, data + size - len, len);
 
-    /* Misc calls on face. */
-    test_face (face, text32[15]);
-  }
+  hb_buffer_t *buffer = hb_buffer_create ();
+  hb_buffer_add_utf32 (buffer, text32, sizeof (text32) / sizeof (text32[0]), 0, -1);
+  hb_buffer_guess_segment_properties (buffer);
+  hb_shape (font, buffer, NULL, 0);
+  hb_buffer_destroy (buffer);
+
+  /* Misc calls on face. */
+  test_face (face, text32[15]);
 
   hb_font_destroy (font);
   hb_face_destroy (face);
commit 517a1bac97b6273e03562deefcca129648698c31
Merge: 9d502769 b18a56a2
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 18:40:48 2018 -0500

    Merge pull request #1362 from harfbuzz/cross-kern
    
    Vastly improve kern/kerx tables, including cross-stream "kerning"

commit b18a56a290bf5330e81019b33f15e6951dd86a8b
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 18:13:22 2018 -0500

    [kerx] Comment

diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh
index cc828149..3cd80acf 100644
--- a/src/hb-aat-layout-kerx-table.hh
+++ b/src/hb-aat-layout-kerx-table.hh
@@ -292,6 +292,10 @@ struct KerxSubTableFormat1
 
 	  hb_glyph_position_t &o = buffer->pos[idx];
 
+	  /* Testing shows that CoreText only applies kern (cross-stream or not)
+	   * if none has been applied by previous subtables.  That is, it does
+	   * NOT seem to accumulate as otherwise implied by specs. */
+
 	  /* The following flag is undocumented in the spec, but described
 	   * in the 'kern' table example. */
 	  if (v == -0x8000)
commit 006386be3a069199ebaf22bcc55fa7233c62e0d5
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 18:04:53 2018 -0500

    [kern] Implement negative state numbers
    
    Let the fuzzing bots rip this code apart...

diff --git a/src/hb-aat-layout-common.hh b/src/hb-aat-layout-common.hh
index 6efc99aa..6a16d70c 100644
--- a/src/hb-aat-layout-common.hh
+++ b/src/hb-aat-layout-common.hh
@@ -430,8 +430,8 @@ struct StateTable
     CLASS_END_OF_LINE = 3,
   };
 
-  inline unsigned int new_state (unsigned int newState) const
-  { return Types::extended ? newState : (newState - stateArrayTable) / nClasses; }
+  inline int new_state (unsigned int newState) const
+  { return Types::extended ? newState : ((int) newState - (int) stateArrayTable) / nClasses; }
 
   inline unsigned int get_class (hb_codepoint_t glyph_id, unsigned int num_glyphs) const
   {
@@ -444,7 +444,7 @@ struct StateTable
     return (this+entryTable).arrayZ;
   }
 
-  inline const Entry<Extra> *get_entryZ (unsigned int state, unsigned int klass) const
+  inline const Entry<Extra> *get_entryZ (int state, unsigned int klass) const
   {
     if (unlikely (klass >= nClasses)) return nullptr;
 
@@ -452,7 +452,7 @@ struct StateTable
     const Entry<Extra> *entries = (this+entryTable).arrayZ;
 
     unsigned int entry = states[state * nClasses + klass];
-    DEBUG_MSG (APPLY, nullptr, "e%d", entry);
+    DEBUG_MSG (APPLY, nullptr, "e%u", entry);
 
     return &entries[entry];
   }
@@ -468,28 +468,69 @@ struct StateTable
     const Entry<Extra> *entries = (this+entryTable).arrayZ;
 
     unsigned int num_classes = nClasses;
+    if (unlikely (hb_unsigned_mul_overflows (num_classes, states[0].static_size)))
+      return_trace (false);
+    unsigned int row_stride = num_classes * states[0].static_size;
 
-    unsigned int num_states = 1;
+    /* Apple 'kern' table has this peculiarity:
+     *
+     * "Because the stateTableOffset in the state table header is (strictly
+     * speaking) redundant, some 'kern' tables use it to record an initial
+     * state where that should not be StartOfText. To determine if this is
+     * done, calculate what the stateTableOffset should be. If it's different
+     * from the actual stateTableOffset, use it as the initial state."
+     *
+     * We implement this by calling the initial state zero, but allow *negative*
+     * states if the start state indeed was not the first state.  Since the code
+     * is shared, this will also apply to 'mort' table.  The 'kerx' / 'morx'
+     * tables are not affected since those address states by index, not offset.
+     */
+
+    int min_state = 0;
+    int max_state = 0;
     unsigned int num_entries = 0;
 
-    unsigned int state = 0;
+    int state_pos = 0;
+    int state_neg = 0;
     unsigned int entry = 0;
-    while (state < num_states)
+    while (min_state < state_neg || state_pos <= max_state)
     {
-      if (unlikely (hb_unsigned_mul_overflows (num_classes, states[0].static_size)))
-	return_trace (false);
+      if (min_state < state_neg)
+      {
+	/* Negative states. */
+	if (unlikely (hb_unsigned_mul_overflows (min_state, num_classes)))
+	  return_trace (false);
+	if (unlikely (!c->check_array (&states[min_state * num_classes], -min_state, row_stride)))
+	  return_trace (false);
+	if ((c->max_ops -= state_neg - min_state) < 0)
+	  return_trace (false);
+	{ /* Sweep new states. */
+	  const HBUSHORT *stop = &states[min_state * num_classes];
+	  if (unlikely (stop > states))
+	    return_trace (false);
+	  for (const HBUSHORT *p = states; stop < p; p--)
+	    num_entries = MAX<unsigned int> (num_entries, *(p - 1) + 1);
+	  state_neg = min_state;
+	}
+      }
 
-      if (unlikely (!c->check_array (states,
-				     num_states,
-				     num_classes * states[0].static_size)))
-	return_trace (false);
-      if ((c->max_ops -= num_states - state) < 0)
-	return_trace (false);
-      { /* Sweep new states. */
-	const HBUSHORT *stop = &states[num_states * num_classes];
-	for (const HBUSHORT *p = &states[state * num_classes]; p < stop; p++)
-	  num_entries = MAX<unsigned int> (num_entries, *p + 1);
-	state = num_states;
+      if (state_pos <= max_state)
+      {
+	/* Positive states. */
+	if (unlikely (!c->check_array (states, max_state + 1, row_stride)))
+	  return_trace (false);
+	if ((c->max_ops -= max_state - state_pos + 1) < 0)
+	  return_trace (false);
+	{ /* Sweep new states. */
+	  if (unlikely (hb_unsigned_mul_overflows ((max_state + 1), num_classes)))
+	    return_trace (false);
+	  const HBUSHORT *stop = &states[(max_state + 1) * num_classes];
+	  if (unlikely (stop < states))
+	    return_trace (false);
+	  for (const HBUSHORT *p = &states[state_pos * num_classes]; p < stop; p++)
+	    num_entries = MAX<unsigned int> (num_entries, *p + 1);
+	  state_pos = max_state + 1;
+	}
       }
 
       if (unlikely (!c->check_array (entries, num_entries)))
@@ -500,8 +541,9 @@ struct StateTable
 	const Entry<Extra> *stop = &entries[num_entries];
 	for (const Entry<Extra> *p = &entries[entry]; p < stop; p++)
 	{
-	  unsigned int newState = new_state (p->newState);
-	  num_states = MAX<unsigned int> (num_states, newState + 1);
+	  int newState = new_state (p->newState);
+	  min_state = MIN (min_state, newState);
+	  max_state = MAX (max_state, newState);
 	}
 	entry = num_entries;
       }
@@ -623,14 +665,14 @@ struct StateTableDriver
     if (!c->in_place)
       buffer->clear_output ();
 
-    unsigned int state = StateTable<Types, EntryData>::STATE_START_OF_TEXT;
+    int state = StateTable<Types, EntryData>::STATE_START_OF_TEXT;
     bool last_was_dont_advance = false;
     for (buffer->idx = 0; buffer->successful;)
     {
       unsigned int klass = buffer->idx < buffer->len ?
 			   machine.get_class (buffer->info[buffer->idx].codepoint, num_glyphs) :
 			   (unsigned) StateTable<Types, EntryData>::CLASS_END_OF_TEXT;
-      DEBUG_MSG (APPLY, nullptr, "c%d at %d", klass, buffer->idx);
+      DEBUG_MSG (APPLY, nullptr, "c%u at %u", klass, buffer->idx);
       const Entry<EntryData> *entry = machine.get_entryZ (state, klass);
       if (unlikely (!entry))
 	break;
diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh
index 272ebfd9..cc828149 100644
--- a/src/hb-aat-layout-kerx-table.hh
+++ b/src/hb-aat-layout-kerx-table.hh
@@ -238,16 +238,6 @@ struct KerxSubTableFormat1
 	depth (0),
 	crossStream (table->header.coverage & table->header.CrossStream) {}
 
-    /* TODO
-     * 'kern' table has this pecularity, we don't currently implement.
-     *
-     * "Because the stateTableOffset in the state table header is (strictly
-     * speaking) redundant, some 'kern' tables use it to record an initial
-     * state where that should not be StartOfText. To determine if this is
-     * done, calculate what the stateTableOffset should be. If it's different
-     * from the actual stateTableOffset, use it as the initial state."
-     */
-
     inline bool is_actionable (StateTableDriver<Types, EntryData> *driver HB_UNUSED,
 			       const Entry<EntryData> *entry)
     {
commit 29c5302376ff2bc8f04b0fc0efba3ce40ef564a7
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 17:29:37 2018 -0500

    [morx] Minor

diff --git a/src/hb-aat-layout-morx-table.hh b/src/hb-aat-layout-morx-table.hh
index 28e3c10d..43073270 100644
--- a/src/hb-aat-layout-morx-table.hh
+++ b/src/hb-aat-layout-morx-table.hh
@@ -461,7 +461,7 @@ struct LigatureSubtable
     {
       hb_buffer_t *buffer = driver->buffer;
 
-      DEBUG_MSG (APPLY, nullptr, "Ligature transition at %d", buffer->idx);
+      DEBUG_MSG (APPLY, nullptr, "Ligature transition at %u", buffer->idx);
       if (entry->flags & LigatureEntryT::SetComponent)
       {
         if (unlikely (match_length >= ARRAY_LENGTH (match_positions)))
@@ -472,12 +472,12 @@ struct LigatureSubtable
 	  match_length--;
 
 	match_positions[match_length++] = buffer->out_len;
-	DEBUG_MSG (APPLY, nullptr, "Set component at %d", buffer->out_len);
+	DEBUG_MSG (APPLY, nullptr, "Set component at %u", buffer->out_len);
       }
 
       if (LigatureEntryT::performAction (entry))
       {
-	DEBUG_MSG (APPLY, nullptr, "Perform action with %d", match_length);
+	DEBUG_MSG (APPLY, nullptr, "Perform action with %u", match_length);
 	unsigned int end = buffer->out_len;
 
 	if (unlikely (!match_length))
@@ -504,7 +504,7 @@ struct LigatureSubtable
 	    break;
 	  }
 
-	  DEBUG_MSG (APPLY, nullptr, "Moving to stack position %d", cursor - 1);
+	  DEBUG_MSG (APPLY, nullptr, "Moving to stack position %u", cursor - 1);
 	  buffer->move_to (match_positions[--cursor]);
 
 	  if (unlikely (!actionData->sanitize (&c->sanitizer))) return false;
@@ -520,7 +520,7 @@ struct LigatureSubtable
 	  if (unlikely (!componentData.sanitize (&c->sanitizer))) return false;
 	  ligature_idx += componentData;
 
-	  DEBUG_MSG (APPLY, nullptr, "Action store %d last %d",
+	  DEBUG_MSG (APPLY, nullptr, "Action store %u last %u",
 		     bool (action & LigActionStore),
 		     bool (action & LigActionLast));
 	  if (action & (LigActionStore | LigActionLast))
@@ -530,7 +530,7 @@ struct LigatureSubtable
 	    if (unlikely (!ligatureData.sanitize (&c->sanitizer))) return false;
 	    hb_codepoint_t lig = ligatureData;
 
-	    DEBUG_MSG (APPLY, nullptr, "Produced ligature %d", lig);
+	    DEBUG_MSG (APPLY, nullptr, "Produced ligature %u", lig);
 	    buffer->replace_glyph (lig);
 
 	    unsigned int lig_end = match_positions[match_length - 1] + 1;
commit 385f78b3123f268e4c7ff423621e5ce9e8a5c54b
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 17:19:21 2018 -0500

    [aat] Remove deleted-glyhs after applying kerx/kern
    
    Finally:  Fixes https://github.com/harfbuzz/harfbuzz/issues/1356
    
    Test case:
    $ ./hb-shape GeezaPro.ttc -u U+0628,U+064A,U+064E,U+0651,U+0629
    [u0629.final.tehMarbuta=4+713|u064e_u0651.shaddaFatha=1 at 0,-200+0|u064a.medial.yeh=1+656|u0628.initial.beh=0+656]
    
    The mark positioning (kern table CrossStream kerning) only works if deleted
    glyph (as result of ligation) is still in stream and pushed through the
    state machine.

diff --git a/src/hb-aat-layout-morx-table.hh b/src/hb-aat-layout-morx-table.hh
index a364f7ac..28e3c10d 100644
--- a/src/hb-aat-layout-morx-table.hh
+++ b/src/hb-aat-layout-morx-table.hh
@@ -1106,21 +1106,6 @@ struct mortmorx
     }
   }
 
-  inline static void remove_deleted_glyphs (hb_buffer_t *buffer)
-  {
-    if (unlikely (!buffer->successful)) return;
-
-    buffer->clear_output ();
-    for (buffer->idx = 0; buffer->idx < buffer->len && buffer->successful;)
-    {
-      if (unlikely (buffer->cur().codepoint == DELETED_GLYPH))
-        buffer->skip_glyph ();
-      else
-        buffer->next_glyph ();
-    }
-    buffer->swap_buffers ();
-  }
-
   inline void apply (hb_aat_apply_context_t *c) const
   {
     if (unlikely (!c->buffer->successful)) return;
@@ -1133,7 +1118,6 @@ struct mortmorx
       if (unlikely (!c->buffer->successful)) return;
       chain = &StructAfter<Chain<Types> > (*chain);
     }
-    remove_deleted_glyphs (c->buffer);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
diff --git a/src/hb-aat-layout.cc b/src/hb-aat-layout.cc
index 5a4e227c..b3b56c08 100644
--- a/src/hb-aat-layout.cc
+++ b/src/hb-aat-layout.cc
@@ -193,7 +193,7 @@ hb_aat_layout_compile_map (const hb_aat_map_builder_t *mapper,
 }
 
 
-hb_bool_t
+bool
 hb_aat_layout_has_substitution (hb_face_t *face)
 {
   return face->table.morx->has_data () ||
@@ -224,8 +224,32 @@ hb_aat_layout_substitute (hb_ot_shape_plan_t *plan,
   }
 }
 
+void
+hb_aat_layout_zero_width_deleted_glyphs (hb_buffer_t *buffer)
+{
+  unsigned int count = buffer->len;
+  hb_glyph_info_t *info = buffer->info;
+  hb_glyph_position_t *pos = buffer->pos;
+  for (unsigned int i = 0; i < count; i++)
+    if (unlikely (info[i].codepoint == AAT::DELETED_GLYPH))
+      pos[i].x_advance = pos[i].y_advance = 0;
+}
+
+static bool
+is_deleted_glyph (const hb_glyph_info_t *info)
+{
+  return info->codepoint == AAT::DELETED_GLYPH;
+}
+
+void
+hb_aat_layout_remove_deleted_glyphs (hb_buffer_t *buffer)
+{
+  hb_ot_layout_delete_glyphs_inplace (buffer, is_deleted_glyph);
+}
+
+
 
-hb_bool_t
+bool
 hb_aat_layout_has_positioning (hb_face_t *face)
 {
   return face->table.kerx->has_data ();
@@ -248,7 +272,7 @@ hb_aat_layout_position (hb_ot_shape_plan_t *plan,
 }
 
 
-hb_bool_t
+bool
 hb_aat_layout_has_tracking (hb_face_t *face)
 {
   return face->table.trak->has_data ();
diff --git a/src/hb-aat-layout.hh b/src/hb-aat-layout.hh
index 8a558e6a..97935a02 100644
--- a/src/hb-aat-layout.hh
+++ b/src/hb-aat-layout.hh
@@ -56,7 +56,7 @@ HB_INTERNAL void
 hb_aat_layout_compile_map (const hb_aat_map_builder_t *mapper,
 			   hb_aat_map_t *map);
 
-HB_INTERNAL hb_bool_t
+HB_INTERNAL bool
 hb_aat_layout_has_substitution (hb_face_t *face);
 
 HB_INTERNAL void
@@ -64,7 +64,13 @@ hb_aat_layout_substitute (hb_ot_shape_plan_t *plan,
 			  hb_font_t *font,
 			  hb_buffer_t *buffer);
 
-HB_INTERNAL hb_bool_t
+HB_INTERNAL void
+hb_aat_layout_zero_width_deleted_glyphs (hb_buffer_t *buffer);
+
+HB_INTERNAL void
+hb_aat_layout_remove_deleted_glyphs (hb_buffer_t *buffer);
+
+HB_INTERNAL bool
 hb_aat_layout_has_positioning (hb_face_t *face);
 
 HB_INTERNAL void
@@ -72,7 +78,7 @@ hb_aat_layout_position (hb_ot_shape_plan_t *plan,
 			hb_font_t *font,
 			hb_buffer_t *buffer);
 
-HB_INTERNAL hb_bool_t
+HB_INTERNAL bool
 hb_aat_layout_has_tracking (hb_face_t *face);
 
 HB_INTERNAL void
diff --git a/src/hb-ot-layout-gpos-table.hh b/src/hb-ot-layout-gpos-table.hh
index 9e1b026d..fc74af47 100644
--- a/src/hb-ot-layout-gpos-table.hh
+++ b/src/hb-ot-layout-gpos-table.hh
@@ -113,7 +113,7 @@ struct ValueFormat : HBUINT16
     if (!format) return ret;
 
     hb_font_t *font = c->font;
-    hb_bool_t horizontal = HB_DIRECTION_IS_HORIZONTAL (c->direction);
+    bool horizontal = HB_DIRECTION_IS_HORIZONTAL (c->direction);
 
     if (format & xPlacement) glyph_pos.x_offset  += font->em_scale_x (get_short (values++, &ret));
     if (format & yPlacement) glyph_pos.y_offset  += font->em_scale_y (get_short (values++, &ret));
@@ -271,10 +271,10 @@ struct AnchorFormat2
     unsigned int x_ppem = font->x_ppem;
     unsigned int y_ppem = font->y_ppem;
     hb_position_t cx = 0, cy = 0;
-    hb_bool_t ret;
+    bool ret;
 
     ret = (x_ppem || y_ppem) &&
-	   font->get_glyph_contour_point_for_origin (glyph_id, anchorPoint, HB_DIRECTION_LTR, &cx, &cy);
+	  font->get_glyph_contour_point_for_origin (glyph_id, anchorPoint, HB_DIRECTION_LTR, &cx, &cy);
     *x = ret && x_ppem ? cx : font->em_fscale_x (xCoordinate);
     *y = ret && y_ppem ? cy : font->em_fscale_y (yCoordinate);
   }
diff --git a/src/hb-ot-layout.cc b/src/hb-ot-layout.cc
index cdc7755a..eb5140fc 100644
--- a/src/hb-ot-layout.cc
+++ b/src/hb-ot-layout.cc
@@ -57,13 +57,13 @@
  * kern
  */
 
-hb_bool_t
+bool
 hb_ot_layout_has_kerning (hb_face_t *face)
 {
   return face->table.kern->has_data ();
 }
 
-hb_bool_t
+bool
 hb_ot_layout_has_cross_kerning (hb_face_t *face)
 {
   return face->table.kern->has_cross_stream ();
@@ -423,7 +423,7 @@ hb_ot_layout_table_get_feature_tags (hb_face_t    *face,
   return g.get_feature_tags (start_offset, feature_count, feature_tags);
 }
 
-hb_bool_t
+bool
 hb_ot_layout_table_find_feature (hb_face_t    *face,
 				 hb_tag_t      table_tag,
 				 hb_tag_t      feature_tag,
@@ -933,12 +933,12 @@ hb_ot_layout_lookup_would_substitute (hb_face_t            *face,
 						    zero_context);
 }
 
-hb_bool_t
+bool
 hb_ot_layout_lookup_would_substitute_fast (hb_face_t            *face,
 					   unsigned int          lookup_index,
 					   const hb_codepoint_t *glyphs,
 					   unsigned int          glyphs_length,
-					   hb_bool_t             zero_context)
+					   bool                  zero_context)
 {
   if (unlikely (lookup_index >= face->table.GSUB->lookup_count)) return false;
   OT::hb_would_apply_context_t c (face, glyphs, glyphs_length, (bool) zero_context);
@@ -955,6 +955,56 @@ hb_ot_layout_substitute_start (hb_font_t    *font,
 _hb_ot_layout_set_glyph_props (font, buffer);
 }
 
+void
+hb_ot_layout_delete_glyphs_inplace (hb_buffer_t *buffer,
+				    bool (*filter) (const hb_glyph_info_t *info))
+{
+  /* Merge clusters and delete filtered glyphs.
+   * NOTE! We can't use out-buffer as we have positioning data. */
+  unsigned int j = 0;
+  unsigned int count = buffer->len;
+  hb_glyph_info_t *info = buffer->info;
+  hb_glyph_position_t *pos = buffer->pos;
+  for (unsigned int i = 0; i < count; i++)
+  {
+    if (filter (&info[i]))
+    {
+      /* Merge clusters.
+       * Same logic as buffer->delete_glyph(), but for in-place removal. */
+
+      unsigned int cluster = info[i].cluster;
+      if (i + 1 < count && cluster == info[i + 1].cluster)
+	continue; /* Cluster survives; do nothing. */
+
+      if (j)
+      {
+	/* Merge cluster backward. */
+	if (cluster < info[j - 1].cluster)
+	{
+	  unsigned int mask = info[i].mask;
+	  unsigned int old_cluster = info[j - 1].cluster;
+	  for (unsigned k = j; k && info[k - 1].cluster == old_cluster; k--)
+	    buffer->set_cluster (info[k - 1], cluster, mask);
+	}
+	continue;
+      }
+
+      if (i + 1 < count)
+	buffer->merge_clusters (i, i + 2); /* Merge cluster forward. */
+
+      continue;
+    }
+
+    if (j != i)
+    {
+      info[j] = info[i];
+      pos[j] = pos[i];
+    }
+    j++;
+  }
+  buffer->len = j;
+}
+
 /**
  * hb_ot_layout_lookup_substitute_closure:
  *
diff --git a/src/hb-ot-layout.hh b/src/hb-ot-layout.hh
index ee6e0d25..b2182531 100644
--- a/src/hb-ot-layout.hh
+++ b/src/hb-ot-layout.hh
@@ -45,10 +45,10 @@ struct hb_ot_shape_plan_t;
  * kern
  */
 
-HB_INTERNAL hb_bool_t
+HB_INTERNAL bool
 hb_ot_layout_has_kerning (hb_face_t *face);
 
-HB_INTERNAL hb_bool_t
+HB_INTERNAL bool
 hb_ot_layout_has_cross_kerning (hb_face_t *face);
 
 HB_INTERNAL void
@@ -59,7 +59,7 @@ hb_ot_layout_kern (hb_ot_shape_plan_t *plan,
 
 /* Private API corresponding to hb-ot-layout.h: */
 
-HB_INTERNAL hb_bool_t
+HB_INTERNAL bool
 hb_ot_layout_table_find_feature (hb_face_t    *face,
 				 hb_tag_t      table_tag,
 				 hb_tag_t      feature_tag,
@@ -93,12 +93,12 @@ HB_MARK_AS_FLAG_T (hb_ot_layout_glyph_props_flags_t);
  * GSUB/GPOS
  */
 
-HB_INTERNAL hb_bool_t
+HB_INTERNAL bool
 hb_ot_layout_lookup_would_substitute_fast (hb_face_t            *face,
 					   unsigned int          lookup_index,
 					   const hb_codepoint_t *glyphs,
 					   unsigned int          glyphs_length,
-					   hb_bool_t             zero_context);
+					   bool                  zero_context);
 
 
 /* Should be called before all the substitute_lookup's are done. */
@@ -106,6 +106,9 @@ HB_INTERNAL void
 hb_ot_layout_substitute_start (hb_font_t    *font,
 			       hb_buffer_t  *buffer);
 
+HB_INTERNAL void
+hb_ot_layout_delete_glyphs_inplace (hb_buffer_t *buffer,
+				    bool (*filter) (const hb_glyph_info_t *info));
 
 namespace OT {
   struct hb_ot_apply_context_t;
@@ -306,13 +309,13 @@ _hb_glyph_info_get_unicode_space_fallback_type (const hb_glyph_info_t *info)
 
 static inline bool _hb_glyph_info_ligated (const hb_glyph_info_t *info);
 
-static inline hb_bool_t
+static inline bool
 _hb_glyph_info_is_default_ignorable (const hb_glyph_info_t *info)
 {
   return (info->unicode_props() & UPROPS_MASK_IGNORABLE) &&
 	 !_hb_glyph_info_ligated (info);
 }
-static inline hb_bool_t
+static inline bool
 _hb_glyph_info_is_default_ignorable_and_not_hidden (const hb_glyph_info_t *info)
 {
   return ((info->unicode_props() & (UPROPS_MASK_IGNORABLE|UPROPS_MASK_HIDDEN))
@@ -366,17 +369,17 @@ _hb_glyph_info_is_unicode_format (const hb_glyph_info_t *info)
   return _hb_glyph_info_get_general_category (info) ==
 	 HB_UNICODE_GENERAL_CATEGORY_FORMAT;
 }
-static inline hb_bool_t
+static inline bool
 _hb_glyph_info_is_zwnj (const hb_glyph_info_t *info)
 {
   return _hb_glyph_info_is_unicode_format (info) && (info->unicode_props() & UPROPS_MASK_Cf_ZWNJ);
 }
-static inline hb_bool_t
+static inline bool
 _hb_glyph_info_is_zwj (const hb_glyph_info_t *info)
 {
   return _hb_glyph_info_is_unicode_format (info) && (info->unicode_props() & UPROPS_MASK_Cf_ZWJ);
 }
-static inline hb_bool_t
+static inline bool
 _hb_glyph_info_is_joiner (const hb_glyph_info_t *info)
 {
   return _hb_glyph_info_is_unicode_format (info) && (info->unicode_props() & (UPROPS_MASK_Cf_ZWNJ|UPROPS_MASK_Cf_ZWJ));
diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
index 9b6d4708..99d6b9d4 100644
--- a/src/hb-ot-shape.cc
+++ b/src/hb-ot-shape.cc
@@ -476,7 +476,9 @@ hb_ensure_native_direction (hb_buffer_t *buffer)
 }
 
 
-/* Substitute */
+/*
+ * Substitute
+ */
 
 static inline void
 hb_ot_mirror_chars (const hb_ot_shape_context_t *c)
@@ -582,10 +584,8 @@ hb_ot_shape_setup_masks (const hb_ot_shape_context_t *c)
 }
 
 static void
-hb_ot_zero_width_default_ignorables (const hb_ot_shape_context_t *c)
+hb_ot_zero_width_default_ignorables (const hb_buffer_t *buffer)
 {
-  hb_buffer_t *buffer = c->buffer;
-
   if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES) ||
       (buffer->flags & HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES) ||
       (buffer->flags & HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES))
@@ -601,21 +601,19 @@ hb_ot_zero_width_default_ignorables (const hb_ot_shape_context_t *c)
 }
 
 static void
-hb_ot_hide_default_ignorables (const hb_ot_shape_context_t *c)
+hb_ot_hide_default_ignorables (hb_buffer_t *buffer,
+			       hb_font_t   *font)
 {
-  hb_buffer_t *buffer = c->buffer;
-
   if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES) ||
       (buffer->flags & HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES))
     return;
 
   unsigned int count = buffer->len;
   hb_glyph_info_t *info = buffer->info;
-  hb_glyph_position_t *pos = buffer->pos;
 
-  hb_codepoint_t invisible = c->buffer->invisible;
+  hb_codepoint_t invisible = buffer->invisible;
   if (!(buffer->flags & HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES) &&
-      (invisible || c->font->get_nominal_glyph (' ', &invisible)))
+      (invisible || font->get_nominal_glyph (' ', &invisible)))
   {
     /* Replace default-ignorables with a zero-advance invisible glyph. */
     for (unsigned int i = 0; i < count; i++)
@@ -625,49 +623,7 @@ hb_ot_hide_default_ignorables (const hb_ot_shape_context_t *c)
     }
   }
   else
-  {
-    /* Merge clusters and delete default-ignorables.
-     * NOTE! We can't use out-buffer as we have positioning data. */
-    unsigned int j = 0;
-    for (unsigned int i = 0; i < count; i++)
-    {
-      if (_hb_glyph_info_is_default_ignorable (&info[i]))
-      {
-	/* Merge clusters.
-	 * Same logic as buffer->delete_glyph(), but for in-place removal. */
-
-	unsigned int cluster = info[i].cluster;
-	if (i + 1 < count && cluster == info[i + 1].cluster)
-	  continue; /* Cluster survives; do nothing. */
-
-	if (j)
-	{
-	  /* Merge cluster backward. */
-	  if (cluster < info[j - 1].cluster)
-	  {
-	    unsigned int mask = info[i].mask;
-	    unsigned int old_cluster = info[j - 1].cluster;
-	    for (unsigned k = j; k && info[k - 1].cluster == old_cluster; k--)
-	      buffer->set_cluster (info[k - 1], cluster, mask);
-	  }
-	  continue;
-	}
-
-	if (i + 1 < count)
-	  buffer->merge_clusters (i, i + 2); /* Merge cluster forward. */
-
-	continue;
-      }
-
-      if (j != i)
-      {
-	info[j] = info[i];
-	pos[j] = pos[i];
-      }
-      j++;
-    }
-    buffer->len = j;
-  }
+    hb_ot_layout_delete_glyphs_inplace (buffer, _hb_glyph_info_is_default_ignorable);
 }
 
 
@@ -684,10 +640,10 @@ hb_ot_map_glyphs_fast (hb_buffer_t  *buffer)
 }
 
 static inline void
-hb_synthesize_glyph_classes (const hb_ot_shape_context_t *c)
+hb_synthesize_glyph_classes (hb_buffer_t *buffer)
 {
-  unsigned int count = c->buffer->len;
-  hb_glyph_info_t *info = c->buffer->info;
+  unsigned int count = buffer->len;
+  hb_glyph_info_t *info = buffer->info;
   for (unsigned int i = 0; i < count; i++)
   {
     hb_ot_layout_glyph_props_flags_t klass;
@@ -739,7 +695,7 @@ hb_ot_substitute_complex (const hb_ot_shape_context_t *c)
   hb_ot_layout_substitute_start (c->font, buffer);
 
   if (c->plan->fallback_glyph_classes)
-    hb_synthesize_glyph_classes (c);
+    hb_synthesize_glyph_classes (c->buffer);
 
   if (unlikely (c->plan->apply_morx))
     hb_aat_layout_substitute (c->plan, c->font, c->buffer);
@@ -748,7 +704,7 @@ hb_ot_substitute_complex (const hb_ot_shape_context_t *c)
 }
 
 static inline void
-hb_ot_substitute (const hb_ot_shape_context_t *c)
+hb_ot_substitute_pre (const hb_ot_shape_context_t *c)
 {
   hb_ot_substitute_default (c);
 
@@ -757,7 +713,21 @@ hb_ot_substitute (const hb_ot_shape_context_t *c)
   hb_ot_substitute_complex (c);
 }
 
-/* Position */
+static inline void
+hb_ot_substitute_post (const hb_ot_shape_context_t *c)
+{
+  hb_ot_hide_default_ignorables (c->buffer, c->font);
+  if (c->plan->apply_morx)
+    hb_aat_layout_remove_deleted_glyphs (c->buffer);
+
+  if (c->plan->shaper->postprocess_glyphs)
+    c->plan->shaper->postprocess_glyphs (c->plan, c->buffer, c->font);
+}
+
+
+/*
+ * Position
+ */
 
 static inline void
 adjust_mark_offsets (hb_glyph_position_t *pos)
@@ -890,9 +860,11 @@ hb_ot_position_complex (const hb_ot_shape_context_t *c)
 	break;
     }
 
-  /* Finishing off GPOS has to follow a certain order. */
+  /* Finish off.  Has to follow a certain order. */
   hb_ot_layout_position_finish_advances (c->font, c->buffer);
-  hb_ot_zero_width_default_ignorables (c);
+  hb_ot_zero_width_default_ignorables (c->buffer);
+  if (c->plan->apply_morx)
+    hb_aat_layout_zero_width_deleted_glyphs (c->buffer);
   hb_ot_layout_position_finish_offsets (c->font, c->buffer);
 
   /* The nil glyph_h_origin() func returns 0, so no need to apply it. */
@@ -983,13 +955,9 @@ hb_ot_shape_internal (hb_ot_shape_context_t *c)
   if (c->plan->shaper->preprocess_text)
     c->plan->shaper->preprocess_text (c->plan, c->buffer, c->font);
 
-  hb_ot_substitute (c);
+  hb_ot_substitute_pre (c);
   hb_ot_position (c);
-
-  hb_ot_hide_default_ignorables (c);
-
-  if (c->plan->shaper->postprocess_glyphs)
-    c->plan->shaper->postprocess_glyphs (c->plan, c->buffer, c->font);
+  hb_ot_substitute_post (c);
 
   hb_propagate_flags (c->buffer);
 
commit 1909072235e59eb80f9169300279b65779b932a4
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 16:42:16 2018 -0500

    [aat] Add debug info to state machine

diff --git a/src/hb-aat-layout-common.hh b/src/hb-aat-layout-common.hh
index 34c61e93..6efc99aa 100644
--- a/src/hb-aat-layout-common.hh
+++ b/src/hb-aat-layout-common.hh
@@ -452,6 +452,7 @@ struct StateTable
     const Entry<Extra> *entries = (this+entryTable).arrayZ;
 
     unsigned int entry = states[state * nClasses + klass];
+    DEBUG_MSG (APPLY, nullptr, "e%d", entry);
 
     return &entries[entry];
   }
@@ -629,6 +630,7 @@ struct StateTableDriver
       unsigned int klass = buffer->idx < buffer->len ?
 			   machine.get_class (buffer->info[buffer->idx].codepoint, num_glyphs) :
 			   (unsigned) StateTable<Types, EntryData>::CLASS_END_OF_TEXT;
+      DEBUG_MSG (APPLY, nullptr, "c%d at %d", klass, buffer->idx);
       const Entry<EntryData> *entry = machine.get_entryZ (state, klass);
       if (unlikely (!entry))
 	break;
@@ -661,6 +663,7 @@ struct StateTableDriver
       last_was_dont_advance = (entry->flags & context_t::DontAdvance) && buffer->max_ops-- > 0;
 
       state = machine.new_state (entry->newState);
+      DEBUG_MSG (APPLY, nullptr, "s%d", state);
 
       if (buffer->idx == buffer->len)
 	break;
commit ca23567f41a2d6389f6fd2483a994cf5aa6aeaf8
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 16:19:51 2018 -0500

    Disable fallback mark positioning if kern table has cross-stream kerning
    
    Happens even if the cross-stream kerning is for cursive attachment only.  Oh well..

diff --git a/src/hb-ot-layout.cc b/src/hb-ot-layout.cc
index 9b93bb08..cdc7755a 100644
--- a/src/hb-ot-layout.cc
+++ b/src/hb-ot-layout.cc
@@ -63,6 +63,12 @@ hb_ot_layout_has_kerning (hb_face_t *face)
   return face->table.kern->has_data ();
 }
 
+hb_bool_t
+hb_ot_layout_has_cross_kerning (hb_face_t *face)
+{
+  return face->table.kern->has_cross_stream ();
+}
+
 void
 hb_ot_layout_kern (hb_ot_shape_plan_t *plan,
 		   hb_font_t *font,
diff --git a/src/hb-ot-layout.hh b/src/hb-ot-layout.hh
index 437ae477..ee6e0d25 100644
--- a/src/hb-ot-layout.hh
+++ b/src/hb-ot-layout.hh
@@ -48,6 +48,9 @@ struct hb_ot_shape_plan_t;
 HB_INTERNAL hb_bool_t
 hb_ot_layout_has_kerning (hb_face_t *face);
 
+HB_INTERNAL hb_bool_t
+hb_ot_layout_has_cross_kerning (hb_face_t *face);
+
 HB_INTERNAL void
 hb_ot_layout_kern (hb_ot_shape_plan_t *plan,
 		   hb_font_t *font,
diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
index 455c8d68..9b6d4708 100644
--- a/src/hb-ot-shape.cc
+++ b/src/hb-ot-shape.cc
@@ -131,13 +131,12 @@ hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t &plan,
       plan.apply_kerx = true;
     else if (hb_ot_layout_has_kerning (face))
       plan.apply_kern = true;
-    else
-      plan.fallback_kerning = true;
   }
 
+  bool has_kern_mark = plan.apply_kern && hb_ot_layout_has_cross_kerning (face);
+  plan.zero_marks = !plan.apply_kerx && !has_kern_mark;
   plan.has_gpos_mark = !!plan.map.get_1_mask (HB_TAG ('m','a','r','k'));
-  if (!plan.apply_gpos && !plan.apply_kerx)
-    plan.fallback_mark_positioning = true;
+  plan.fallback_mark_positioning = !plan.apply_gpos && !plan.apply_kerx && !has_kern_mark;
 
   /* Currently we always apply trak. */
   plan.apply_trak = plan.requested_tracking && hb_aat_layout_has_tracking (face);
@@ -853,7 +852,7 @@ hb_ot_position_complex (const hb_ot_shape_context_t *c)
 
   hb_ot_layout_position_start (c->font, c->buffer);
 
-  if (!c->plan->apply_kerx)
+  if (c->plan->zero_marks)
     switch (c->plan->shaper->zero_width_marks)
     {
       case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY:
@@ -866,20 +865,19 @@ hb_ot_position_complex (const hb_ot_shape_context_t *c)
 	break;
     }
 
-  /* XXX Clean up relationship between these. */
   if (c->plan->apply_gpos)
     c->plan->position (c->font, c->buffer);
   else if (c->plan->apply_kerx)
     hb_aat_layout_position (c->plan, c->font, c->buffer);
   else if (c->plan->apply_kern)
     hb_ot_layout_kern (c->plan, c->font, c->buffer);
-  else if (c->plan->fallback_kerning)
+  else
     _hb_ot_shape_fallback_kern (c->plan, c->font, c->buffer);
 
   if (c->plan->apply_trak)
     hb_aat_layout_track (c->plan, c->font, c->buffer);
 
-  if (!c->plan->apply_kerx)
+  if (c->plan->zero_marks)
     switch (c->plan->shaper->zero_width_marks)
     {
       case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE:
diff --git a/src/hb-ot-shape.hh b/src/hb-ot-shape.hh
index 1cb9e24d..655e28d6 100644
--- a/src/hb-ot-shape.hh
+++ b/src/hb-ot-shape.hh
@@ -51,8 +51,8 @@ struct hb_ot_shape_plan_t
   bool requested_tracking : 1;
   bool has_frac : 1;
   bool has_gpos_mark : 1;
+  bool zero_marks : 1;
   bool fallback_glyph_classes : 1;
-  bool fallback_kerning : 1;
   bool fallback_mark_positioning : 1;
 
   bool apply_gpos : 1;
commit 5cf6f94dfd30a468ab8464435e846811c39d9226
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 16:07:22 2018 -0500

    Don't apply both kerx and kern
    
    Ouch!

diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
index dfe2a88c..455c8d68 100644
--- a/src/hb-ot-shape.cc
+++ b/src/hb-ot-shape.cc
@@ -129,7 +129,7 @@ hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t &plan,
     /* Apparently Apple applies kerx if GPOS kern was not applied. */
     if (hb_aat_layout_has_positioning (face))
       plan.apply_kerx = true;
-    if (hb_ot_layout_has_kerning (face))
+    else if (hb_ot_layout_has_kerning (face))
       plan.apply_kern = true;
     else
       plan.fallback_kerning = true;
commit 41cff7afc916048810a7ea4aa33ecdee7401df74
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 16:05:36 2018 -0500

    Minor

diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
index bc4c2498..dfe2a88c 100644
--- a/src/hb-ot-shape.cc
+++ b/src/hb-ot-shape.cc
@@ -903,6 +903,9 @@ hb_ot_position_complex (const hb_ot_shape_context_t *c)
       c->font->subtract_glyph_h_origin (info[i].codepoint,
 					&pos[i].x_offset,
 					&pos[i].y_offset);
+
+  if (c->plan->fallback_mark_positioning && c->plan->shaper->fallback_position)
+    _hb_ot_shape_fallback_mark_position (c->plan, c->font, c->buffer);
 }
 
 static inline void
@@ -914,9 +917,6 @@ hb_ot_position (const hb_ot_shape_context_t *c)
 
   hb_ot_position_complex (c);
 
-  if (c->plan->fallback_mark_positioning && c->plan->shaper->fallback_position)
-    _hb_ot_shape_fallback_mark_position (c->plan, c->font, c->buffer);
-
   if (HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction))
     hb_buffer_reverse (c->buffer);
 
commit 9af983af24788afad4b37bd2297b86cdca7c5c29
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 16:03:09 2018 -0500

    [kern] Switch to dispatch

diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh
index 42212094..272ebfd9 100644
--- a/src/hb-aat-layout-kerx-table.hh
+++ b/src/hb-aat-layout-kerx-table.hh
@@ -881,10 +881,11 @@ struct KerxTable
     return v;
   }
 
-  inline void apply (AAT::hb_aat_apply_context_t *c) const
+  inline bool apply (AAT::hb_aat_apply_context_t *c) const
   {
     typedef typename T::SubTable SubTable;
 
+    bool ret = false;
     bool seenCrossStream = false;
     c->set_lookup_index (0);
     const SubTable *st = &thiz()->firstSubTable;
@@ -927,7 +928,7 @@ struct KerxTable
 
       c->sanitizer.set_object (*st);
 
-      st->dispatch (c);
+      ret |= st->dispatch (c);
 
       if (reverse)
 	c->buffer->reverse ();
@@ -938,6 +939,8 @@ struct KerxTable
       st = &StructAfter<SubTable> (*st);
       c->set_lookup_index (c->lookup_index + 1);
     }
+
+    return ret;
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
diff --git a/src/hb-ot-kern-table.hh b/src/hb-ot-kern-table.hh
index 690532e2..14e06568 100644
--- a/src/hb-ot-kern-table.hh
+++ b/src/hb-ot-kern-table.hh
@@ -272,10 +272,11 @@ struct kern
   static const hb_tag_t tableTag = HB_OT_TAG_kern;
 
   inline bool has_data (void) const { return u.version32; }
+  inline unsigned int get_type (void) const { return u.major; }
 
   inline bool has_cross_stream (void) const
   {
-    switch (u.major) {
+    switch (get_type ()) {
     case 0: return u.ot.has_cross_stream ();
     case 1: return u.aat.has_cross_stream ();
     default:return false;
@@ -284,20 +285,25 @@ struct kern
 
   inline int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right) const
   {
-    switch (u.major) {
+    switch (get_type ()) {
     case 0: return u.ot.get_h_kerning (left, right);
     case 1: return u.aat.get_h_kerning (left, right);
     default:return 0;
     }
   }
 
-  inline void apply (AAT::hb_aat_apply_context_t *c) const
+  inline bool apply (AAT::hb_aat_apply_context_t *c) const
+  { return dispatch (c); }
+
+  template <typename context_t>
+  inline typename context_t::return_t dispatch (context_t *c) const
   {
-    /* TODO Switch to dispatch(). */
-    switch (u.major) {
-    case 0: u.ot.apply (c);  return;
-    case 1: u.aat.apply (c); return;
-    default:		     return;
+    unsigned int subtable_type = get_type ();
+    TRACE_DISPATCH (this, subtable_type);
+    switch (subtable_type) {
+    case 0:	return_trace (c->dispatch (u.ot));
+    case 1:	return_trace (c->dispatch (u.aat));
+    default:	return_trace (c->default_return_value ());
     }
   }
 
@@ -305,11 +311,7 @@ struct kern
   {
     TRACE_SANITIZE (this);
     if (!u.version32.sanitize (c)) return_trace (false);
-    switch (u.major) {
-    case 0: return_trace (u.ot.sanitize (c));
-    case 1: return_trace (u.aat.sanitize (c));
-    default:return_trace (true);
-    }
+    return_trace (dispatch (c));
   }
 
   protected:
diff --git a/src/hb-ot-layout.cc b/src/hb-ot-layout.cc
index 6da1d3fd..9b93bb08 100644
--- a/src/hb-ot-layout.cc
+++ b/src/hb-ot-layout.cc
@@ -1275,10 +1275,8 @@ apply_backward (OT::hb_ot_apply_context_t *c,
     if (accel.may_have (buffer->cur().codepoint) &&
 	(buffer->cur().mask & c->lookup_mask) &&
 	c->check_glyph_property (&buffer->cur(), c->lookup_props))
-    {
-     if (accel.apply (c))
-       ret = true;
-    }
+     ret |= accel.apply (c);
+
     /* The reverse lookup doesn't "advance" cursor (for good reason). */
     buffer->idx--;
 
commit bc06e2805ae55f5c152dfb70ee91c75830ad8f54
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 16:02:40 2018 -0500

    [kerx/kern] Add has_cross_stream()

diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh
index 84fa6dcd..42212094 100644
--- a/src/hb-aat-layout-kerx-table.hh
+++ b/src/hb-aat-layout-kerx-table.hh
@@ -848,6 +848,21 @@ struct KerxTable
   /* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */
   inline const T* thiz (void) const { return static_cast<const T *> (this); }
 
+  inline bool has_cross_stream (void) const
+  {
+    typedef typename T::SubTable SubTable;
+
+    const SubTable *st = &thiz()->firstSubTable;
+    unsigned int count = thiz()->tableCount;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      if (st->u.header.coverage & st->u.header.CrossStream)
+        return true;
+      st = &StructAfter<SubTable> (*st);
+    }
+    return false;
+  }
+
   inline int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right) const
   {
     typedef typename T::SubTable SubTable;
diff --git a/src/hb-ot-kern-table.hh b/src/hb-ot-kern-table.hh
index 95306ece..690532e2 100644
--- a/src/hb-ot-kern-table.hh
+++ b/src/hb-ot-kern-table.hh
@@ -273,6 +273,15 @@ struct kern
 
   inline bool has_data (void) const { return u.version32; }
 
+  inline bool has_cross_stream (void) const
+  {
+    switch (u.major) {
+    case 0: return u.ot.has_cross_stream ();
+    case 1: return u.aat.has_cross_stream ();
+    default:return false;
+    }
+  }
+
   inline int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right) const
   {
     switch (u.major) {
commit ea579f9ccc87718d4c2ca8945a997e6679428a12
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 15:44:40 2018 -0500

    [kerx] Fix peculiar indexing that was needed previously
    
    Not needed now that we use GPOS attachment for cursive kerx.

diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh
index dc3ecc58..84fa6dcd 100644
--- a/src/hb-aat-layout-kerx-table.hh
+++ b/src/hb-aat-layout-kerx-table.hh
@@ -286,23 +286,18 @@ struct KerxSubTableFormat1
 	/* From Apple 'kern' spec:
 	 * "Each pops one glyph from the kerning stack and applies the kerning value to it.
 	 * The end of the list is marked by an odd value... */
-	unsigned int i;
-	for (i = 0; i < depth; i++)
-	  if (actions[i] & 1)
-	  {
-	    i++;
-	    break;
-	  }
 	unsigned int tuple_count = table->header.tuple_count ();
 	tuple_count = tuple_count ? tuple_count : 1;
-	for (; i; i--)
+	bool last = false;
+	while (!last && depth--)
 	{
-	  unsigned int idx = stack[depth - i];
+	  unsigned int idx = stack[depth];
+	  int v = *actions;
+	  actions += tuple_count;
 	  if (idx >= buffer->len) continue;
 
-	  int v = actions[(i - 1) * tuple_count];
-
-	  /* "The end of the list is marked by an odd value..."  Ignore it. */
+	  /* "The end of the list is marked by an odd value..." */
+	  last = v & 1;
 	  v &= ~1;
 
 	  hb_glyph_position_t &o = buffer->pos[idx];
@@ -355,7 +350,6 @@ struct KerxSubTableFormat1
 	    }
 	  }
 	}
-	depth = 0;
       }
 
       return true;
commit 6ee6cd93d8c61389cf242e42a531cc6e7214b21a
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 15:40:55 2018 -0500

    [GPOS] Only mark unsafe-to-break if kerning happened
    
    Fixes https://github.com/harfbuzz/harfbuzz/issues/1365

diff --git a/src/hb-ot-layout-gpos-table.hh b/src/hb-ot-layout-gpos-table.hh
index 0dcbb5a0..9e1b026d 100644
--- a/src/hb-ot-layout-gpos-table.hh
+++ b/src/hb-ot-layout-gpos-table.hh
@@ -103,56 +103,58 @@ struct ValueFormat : HBUINT16
   inline unsigned int get_size (void) const
   { return get_len () * Value::static_size; }
 
-  void apply_value (hb_ot_apply_context_t   *c,
+  bool apply_value (hb_ot_apply_context_t   *c,
 		    const void           *base,
 		    const Value          *values,
 		    hb_glyph_position_t  &glyph_pos) const
   {
+    bool ret = false;
     unsigned int format = *this;
-    if (!format) return;
+    if (!format) return ret;
 
     hb_font_t *font = c->font;
     hb_bool_t horizontal = HB_DIRECTION_IS_HORIZONTAL (c->direction);
 
-    if (format & xPlacement) glyph_pos.x_offset  += font->em_scale_x (get_short (values++));
-    if (format & yPlacement) glyph_pos.y_offset  += font->em_scale_y (get_short (values++));
+    if (format & xPlacement) glyph_pos.x_offset  += font->em_scale_x (get_short (values++, &ret));
+    if (format & yPlacement) glyph_pos.y_offset  += font->em_scale_y (get_short (values++, &ret));
     if (format & xAdvance) {
-      if (likely (horizontal)) glyph_pos.x_advance += font->em_scale_x (get_short (values));
+      if (likely (horizontal)) glyph_pos.x_advance += font->em_scale_x (get_short (values, &ret));
       values++;
     }
     /* y_advance values grow downward but font-space grows upward, hence negation */
     if (format & yAdvance) {
-      if (unlikely (!horizontal)) glyph_pos.y_advance -= font->em_scale_y (get_short (values));
+      if (unlikely (!horizontal)) glyph_pos.y_advance -= font->em_scale_y (get_short (values, &ret));
       values++;
     }
 
-    if (!has_device ()) return;
+    if (!has_device ()) return ret;
 
     bool use_x_device = font->x_ppem || font->num_coords;
     bool use_y_device = font->y_ppem || font->num_coords;
 
-    if (!use_x_device && !use_y_device) return;
+    if (!use_x_device && !use_y_device) return ret;
 
     const VariationStore &store = c->var_store;
 
     /* pixel -> fractional pixel */
     if (format & xPlaDevice) {
-      if (use_x_device) glyph_pos.x_offset  += (base + get_device (values)).get_x_delta (font, store);
+      if (use_x_device) glyph_pos.x_offset  += (base + get_device (values, &ret)).get_x_delta (font, store);
       values++;
     }
     if (format & yPlaDevice) {
-      if (use_y_device) glyph_pos.y_offset  += (base + get_device (values)).get_y_delta (font, store);
+      if (use_y_device) glyph_pos.y_offset  += (base + get_device (values, &ret)).get_y_delta (font, store);
       values++;
     }
     if (format & xAdvDevice) {
-      if (horizontal && use_x_device) glyph_pos.x_advance += (base + get_device (values)).get_x_delta (font, store);
+      if (horizontal && use_x_device) glyph_pos.x_advance += (base + get_device (values, &ret)).get_x_delta (font, store);
       values++;
     }
     if (format & yAdvDevice) {
       /* y_advance values grow downward but font-space grows upward, hence negation */
-      if (!horizontal && use_y_device) glyph_pos.y_advance -= (base + get_device (values)).get_y_delta (font, store);
+      if (!horizontal && use_y_device) glyph_pos.y_advance -= (base + get_device (values, &ret)).get_y_delta (font, store);
       values++;
     }
+    return ret;
   }
 
   private:
@@ -175,11 +177,17 @@ struct ValueFormat : HBUINT16
 
   static inline OffsetTo<Device>& get_device (Value* value)
   { return *CastP<OffsetTo<Device> > (value); }
-  static inline const OffsetTo<Device>& get_device (const Value* value)
-  { return *CastP<OffsetTo<Device> > (value); }
+  static inline const OffsetTo<Device>& get_device (const Value* value, bool *worked=nullptr)
+  {
+    if (worked) *worked |= *value;
+    return *CastP<OffsetTo<Device> > (value);
+  }
 
-  static inline const HBINT16& get_short (const Value* value)
-  { return *CastP<HBINT16> (value); }
+  static inline const HBINT16& get_short (const Value* value, bool *worked=nullptr)
+  {
+    if (worked) *worked |= *value;
+    return *CastP<HBINT16> (value);
+  }
 
   public:
 
@@ -672,9 +680,10 @@ struct PairSet
         min = mid + 1;
       else
       {
-        buffer->unsafe_to_break (buffer->idx, pos + 1);
-	valueFormats[0].apply_value (c, this, &record->values[0], buffer->cur_pos());
-	valueFormats[1].apply_value (c, this, &record->values[len1], buffer->pos[pos]);
+	/* Note the intentional use of "|" instead of short-circuit "||". */
+	if (valueFormats[0].apply_value (c, this, &record->values[0], buffer->cur_pos()) |
+	    valueFormats[1].apply_value (c, this, &record->values[len1], buffer->pos[pos]))
+	  buffer->unsafe_to_break (buffer->idx, pos + 1);
 	if (len2)
 	  pos++;
 	buffer->idx = pos;
@@ -837,12 +846,11 @@ struct PairPosFormat2
     unsigned int klass2 = (this+classDef2).get_class (buffer->info[skippy_iter.idx].codepoint);
     if (unlikely (klass1 >= class1Count || klass2 >= class2Count)) return_trace (false);
 
-    /* TODO Only unsafe_to_break if kerning values not zero...
-     * https://github.com/harfbuzz/harfbuzz/issues/1365 */
-    buffer->unsafe_to_break (buffer->idx, skippy_iter.idx + 1);
     const Value *v = &values[record_len * (klass1 * class2Count + klass2)];
-    valueFormat1.apply_value (c, this, v, buffer->cur_pos());
-    valueFormat2.apply_value (c, this, v + len1, buffer->pos[skippy_iter.idx]);
+    /* Note the intentional use of "|" instead of short-circuit "||". */
+    if (valueFormat1.apply_value (c, this, v, buffer->cur_pos()) |
+	valueFormat2.apply_value (c, this, v + len1, buffer->pos[skippy_iter.idx]))
+      buffer->unsafe_to_break (buffer->idx, skippy_iter.idx + 1);
 
     buffer->idx = skippy_iter.idx;
     if (len2)
commit 501a364d9bb6c5828f9d660bae8b6e93b158b275
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 15:02:16 2018 -0500

    [GPOS] Add TODO item

diff --git a/src/hb-ot-layout-gpos-table.hh b/src/hb-ot-layout-gpos-table.hh
index 743a9793..0dcbb5a0 100644
--- a/src/hb-ot-layout-gpos-table.hh
+++ b/src/hb-ot-layout-gpos-table.hh
@@ -837,6 +837,8 @@ struct PairPosFormat2
     unsigned int klass2 = (this+classDef2).get_class (buffer->info[skippy_iter.idx].codepoint);
     if (unlikely (klass1 >= class1Count || klass2 >= class2Count)) return_trace (false);
 
+    /* TODO Only unsafe_to_break if kerning values not zero...
+     * https://github.com/harfbuzz/harfbuzz/issues/1365 */
     buffer->unsafe_to_break (buffer->idx, skippy_iter.idx + 1);
     const Value *v = &values[record_len * (klass1 * class2Count + klass2)];
     valueFormat1.apply_value (c, this, v, buffer->cur_pos());
commit 7a9629f2f11a11d1c064662a08a0172ac2001668
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 14:52:36 2018 -0500

    [kerx] Implement CrossStream kerning for non-state-machine subtables
    
    Untested.

diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh
index 222285d4..dc3ecc58 100644
--- a/src/hb-aat-layout-kerx-table.hh
+++ b/src/hb-aat-layout-kerx-table.hh
@@ -118,11 +118,11 @@ struct KerxSubTableFormat0
     if (!c->plan->requested_kerning)
       return false;
 
-    if (header.coverage & (header.CrossStream | header.Backwards))
+    if (header.coverage & header.Backwards)
       return false;
 
     accelerator_t accel (*this, c);
-    hb_kern_machine_t<accelerator_t> machine (accel);
+    hb_kern_machine_t<accelerator_t> machine (accel, header.coverage & header.CrossStream);
     machine.kern (c->font, c->buffer, c->plan->kern_mask);
 
     return_trace (true);
@@ -427,11 +427,11 @@ struct KerxSubTableFormat2
     if (!c->plan->requested_kerning)
       return false;
 
-    if (header.coverage & (header.CrossStream | header.Backwards))
+    if (header.coverage & header.Backwards)
       return false;
 
     accelerator_t accel (*this, c);
-    hb_kern_machine_t<accelerator_t> machine (accel);
+    hb_kern_machine_t<accelerator_t> machine (accel, header.coverage & header.CrossStream);
     machine.kern (c->font, c->buffer, c->plan->kern_mask);
 
     return_trace (true);
@@ -696,11 +696,11 @@ struct KerxSubTableFormat6
     if (!c->plan->requested_kerning)
       return false;
 
-    if (header.coverage & (header.CrossStream | header.Backwards))
+    if (header.coverage & header.Backwards)
       return false;
 
     accelerator_t accel (*this, c);
-    hb_kern_machine_t<accelerator_t> machine (accel);
+    hb_kern_machine_t<accelerator_t> machine (accel, header.coverage & header.CrossStream);
     machine.kern (c->font, c->buffer, c->plan->kern_mask);
 
     return_trace (true);
diff --git a/src/hb-kern.hh b/src/hb-kern.hh
index 60e625c4..aa01b470 100644
--- a/src/hb-kern.hh
+++ b/src/hb-kern.hh
@@ -38,7 +38,10 @@ namespace OT {
 template <typename Driver>
 struct hb_kern_machine_t
 {
-  hb_kern_machine_t (const Driver &driver_) : driver (driver_) {}
+  hb_kern_machine_t (const Driver &driver_,
+		     bool crossStream_ = false) :
+		       driver (driver_),
+		       crossStream (crossStream_) {}
 
   HB_NO_SANITIZE_SIGNED_INTEGER_OVERFLOW
   inline void kern (hb_font_t   *font,
@@ -81,26 +84,41 @@ struct hb_kern_machine_t
       if (likely (!kern))
         goto skip;
 
-
       if (horizontal)
       {
         if (scale)
 	  kern = font->em_scale_x (kern);
-	hb_position_t kern1 = kern >> 1;
-	hb_position_t kern2 = kern - kern1;
-	pos[i].x_advance += kern1;
-	pos[j].x_advance += kern2;
-	pos[j].x_offset += kern2;
+	if (crossStream)
+	{
+	  pos[j].y_offset = kern;
+	  buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
+	}
+	else
+	{
+	  hb_position_t kern1 = kern >> 1;
+	  hb_position_t kern2 = kern - kern1;
+	  pos[i].x_advance += kern1;
+	  pos[j].x_advance += kern2;
+	  pos[j].x_offset += kern2;
+	}
       }
       else
       {
         if (scale)
 	  kern = font->em_scale_y (kern);
-	hb_position_t kern1 = kern >> 1;
-	hb_position_t kern2 = kern - kern1;
-	pos[i].y_advance += kern1;
-	pos[j].y_advance += kern2;
-	pos[j].y_offset += kern2;
+	if (crossStream)
+	{
+	  pos[j].x_offset = kern;
+	  buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
+	}
+	else
+	{
+	  hb_position_t kern1 = kern >> 1;
+	  hb_position_t kern2 = kern - kern1;
+	  pos[i].y_advance += kern1;
+	  pos[j].y_advance += kern2;
+	  pos[j].y_offset += kern2;
+	}
       }
 
       buffer->unsafe_to_break (i, j + 1);
@@ -111,6 +129,7 @@ struct hb_kern_machine_t
   }
 
   const Driver &driver;
+  bool crossStream;
 };
 
 
diff --git a/src/hb-ot-kern-table.hh b/src/hb-ot-kern-table.hh
index b2c29e30..95306ece 100644
--- a/src/hb-ot-kern-table.hh
+++ b/src/hb-ot-kern-table.hh
@@ -66,10 +66,10 @@ struct KernSubTableFormat3
     if (!c->plan->requested_kerning)
       return false;
 
-    if (header.coverage & (header.CrossStream | header.Backwards))
+    if (header.coverage & header.Backwards)
       return false;
 
-    hb_kern_machine_t<KernSubTableFormat3> machine (*this);
+    hb_kern_machine_t<KernSubTableFormat3> machine (*this, header.coverage & header.CrossStream);
     machine.kern (c->font, c->buffer, c->plan->kern_mask);
 
     return_trace (true);
commit 0eb4157011e78c332d781e28b54b020aa08957c0
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 14:42:15 2018 -0500

    [kerx] Disable backwards-kerning for non-state-machine tables
    
    That's what the spec says for Backwards flag, only applicable to
    formats 1 and 4.

diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh
index 4ef5d9e2..222285d4 100644
--- a/src/hb-aat-layout-kerx-table.hh
+++ b/src/hb-aat-layout-kerx-table.hh
@@ -118,7 +118,7 @@ struct KerxSubTableFormat0
     if (!c->plan->requested_kerning)
       return false;
 
-    if (header.coverage & header.CrossStream)
+    if (header.coverage & (header.CrossStream | header.Backwards))
       return false;
 
     accelerator_t accel (*this, c);
@@ -427,7 +427,7 @@ struct KerxSubTableFormat2
     if (!c->plan->requested_kerning)
       return false;
 
-    if (header.coverage & header.CrossStream)
+    if (header.coverage & (header.CrossStream | header.Backwards))
       return false;
 
     accelerator_t accel (*this, c);
@@ -696,7 +696,7 @@ struct KerxSubTableFormat6
     if (!c->plan->requested_kerning)
       return false;
 
-    if (header.coverage & header.CrossStream)
+    if (header.coverage & (header.CrossStream | header.Backwards))
       return false;
 
     accelerator_t accel (*this, c);
@@ -918,8 +918,6 @@ struct KerxTable
 
       c->sanitizer.set_object (*st);
 
-      /* XXX Reverse-kern is probably not working yet...
-       * hb_kern_machine_t would need to know that it's reverse-kerning. */
       st->dispatch (c);
 
       if (reverse)
diff --git a/src/hb-ot-kern-table.hh b/src/hb-ot-kern-table.hh
index b90f7fba..b2c29e30 100644
--- a/src/hb-ot-kern-table.hh
+++ b/src/hb-ot-kern-table.hh
@@ -66,7 +66,7 @@ struct KernSubTableFormat3
     if (!c->plan->requested_kerning)
       return false;
 
-    if (header.coverage & header.CrossStream)
+    if (header.coverage & (header.CrossStream | header.Backwards))
       return false;
 
     hb_kern_machine_t<KernSubTableFormat3> machine (*this);
commit b2f687c2569a3cc0b1cd0335c5ca0f8d193f8a39
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 14:38:29 2018 -0500

    [kerx] Use GPOS attachment facilities for CrossStream kerning

diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh
index 09c72087..4ef5d9e2 100644
--- a/src/hb-aat-layout-kerx-table.hh
+++ b/src/hb-aat-layout-kerx-table.hh
@@ -236,9 +236,7 @@ struct KerxSubTableFormat1
 	 * other subtables in kerx.  Discovered via testing. */
 	kernAction (&table->machine + table->kernAction),
 	depth (0),
-	crossStream (table->header.coverage & table->header.CrossStream),
-	crossOffset (0) {}
-
+	crossStream (table->header.coverage & table->header.CrossStream) {}
 
     /* TODO
      * 'kern' table has this pecularity, we don't currently implement.
@@ -307,21 +305,25 @@ struct KerxSubTableFormat1
 	  /* "The end of the list is marked by an odd value..."  Ignore it. */
 	  v &= ~1;
 
+	  hb_glyph_position_t &o = buffer->pos[idx];
+
 	  /* The following flag is undocumented in the spec, but described
 	   * in the 'kern' table example. */
 	  if (v == -0x8000)
 	  {
-	    crossOffset = 0;
-	    v = 0;
+	    o.attach_type() = ATTACH_TYPE_NONE;
+	    o.attach_chain() = 0;
+	    o.x_offset = o.y_offset = 0;
 	  }
-
-	  if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
+	  else if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
 	  {
 	    if (crossStream)
 	    {
-	      crossOffset += v;
-	      if (!buffer->pos[idx].y_offset)
-		buffer->pos[idx].y_offset += c->font->em_scale_y (crossOffset);
+	      if (buffer->pos[idx].attach_type() && !buffer->pos[idx].y_offset)
+	      {
+		o.y_offset = c->font->em_scale_y (v);
+		buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
+	      }
 	    }
 	    else if (buffer->info[idx].mask & kern_mask)
 	    {
@@ -337,9 +339,11 @@ struct KerxSubTableFormat1
 	    if (crossStream)
 	    {
 	      /* CoreText doesn't do crossStream kerning in vertical.  We do. */
-	      crossOffset += v;
-	      if (!buffer->pos[idx].x_offset)
-		buffer->pos[idx].x_offset = c->font->em_scale_x (crossOffset);
+	      if (buffer->pos[idx].attach_type() && !buffer->pos[idx].x_offset)
+	      {
+		o.x_offset = c->font->em_scale_x (v);
+		buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
+	      }
 	    }
 	    else if (buffer->info[idx].mask & kern_mask)
 	    {
@@ -353,8 +357,6 @@ struct KerxSubTableFormat1
 	}
 	depth = 0;
       }
-      else
-	buffer->pos[buffer->idx].y_offset += c->font->em_scale_y (crossOffset);
 
       return true;
     }
@@ -366,7 +368,6 @@ struct KerxSubTableFormat1
     unsigned int stack[8];
     unsigned int depth;
     bool crossStream;
-    int crossOffset;
   };
 
   inline bool apply (hb_aat_apply_context_t *c) const
@@ -875,6 +876,7 @@ struct KerxTable
   {
     typedef typename T::SubTable SubTable;
 
+    bool seenCrossStream = false;
     c->set_lookup_index (0);
     const SubTable *st = &thiz()->firstSubTable;
     unsigned int count = thiz()->tableCount;
@@ -894,6 +896,23 @@ struct KerxTable
       if (!c->buffer->message (c->font, "start %c%c%c%c subtable %d", HB_UNTAG (thiz()->tableTag), c->lookup_index))
 	goto skip;
 
+      if (!seenCrossStream &&
+	  (st->u.header.coverage & st->u.header.CrossStream))
+      {
+        /* Attach all glyphs into a chain. */
+        seenCrossStream = true;
+	hb_glyph_position_t *pos = c->buffer->pos;
+	unsigned int count = c->buffer->len;
+	for (unsigned int i = 0; i < count; i++)
+	{
+	  pos[i].attach_type() = ATTACH_TYPE_CURSIVE;
+	  pos[i].attach_chain() = HB_DIRECTION_IS_FORWARD (c->buffer->props.direction) ? -1 : +1;
+	  /* We intentionally don't set HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT,
+	   * since there needs to be a non-zero attachment for post-positioning to
+	   * be needed. */
+	}
+      }
+
       if (reverse)
 	c->buffer->reverse ();
 
commit e10a856eb24ae45e301c3ffa778caa4c0a995bb9
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 14:11:48 2018 -0500

    [kerx] Format

diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh
index aabbd4c9..09c72087 100644
--- a/src/hb-aat-layout-kerx-table.hh
+++ b/src/hb-aat-layout-kerx-table.hh
@@ -300,10 +300,11 @@ struct KerxSubTableFormat1
 	for (; i; i--)
 	{
 	  unsigned int idx = stack[depth - i];
+	  if (idx >= buffer->len) continue;
+
 	  int v = actions[(i - 1) * tuple_count];
 
-	  /* "The end of the list is marked by an odd value..."
-	   * Ignore it. */
+	  /* "The end of the list is marked by an odd value..."  Ignore it. */
 	  v &= ~1;
 
 	  /* The following flag is undocumented in the spec, but described
@@ -314,41 +315,38 @@ struct KerxSubTableFormat1
 	    v = 0;
 	  }
 
-	  if (idx < buffer->len)
+	  if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
 	  {
-	    if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
+	    if (crossStream)
 	    {
-	      if (crossStream)
-	      {
-		crossOffset += v;
-		if (!buffer->pos[idx].y_offset)
-		  buffer->pos[idx].y_offset += c->font->em_scale_y (crossOffset);
-	      }
-	      else if (buffer->info[idx].mask & kern_mask)
-	      {
-		if (!buffer->pos[idx].x_offset)
-		{
-		  buffer->pos[idx].x_advance += c->font->em_scale_x (v);
-		  buffer->pos[idx].x_offset += c->font->em_scale_x (v);
-		}
-	      }
+	      crossOffset += v;
+	      if (!buffer->pos[idx].y_offset)
+		buffer->pos[idx].y_offset += c->font->em_scale_y (crossOffset);
 	    }
-	    else
+	    else if (buffer->info[idx].mask & kern_mask)
 	    {
-	      if (crossStream)
+	      if (!buffer->pos[idx].x_offset)
 	      {
-	        /* CoreText doesn't do crossStream kerning in vertical.  We do. */
-		crossOffset += v;
-		if (!buffer->pos[idx].x_offset)
-		  buffer->pos[idx].x_offset = c->font->em_scale_x (crossOffset);
+		buffer->pos[idx].x_advance += c->font->em_scale_x (v);
+		buffer->pos[idx].x_offset += c->font->em_scale_x (v);
 	      }
-	      else if (buffer->info[idx].mask & kern_mask)
+	    }
+	  }
+	  else
+	  {
+	    if (crossStream)
+	    {
+	      /* CoreText doesn't do crossStream kerning in vertical.  We do. */
+	      crossOffset += v;
+	      if (!buffer->pos[idx].x_offset)
+		buffer->pos[idx].x_offset = c->font->em_scale_x (crossOffset);
+	    }
+	    else if (buffer->info[idx].mask & kern_mask)
+	    {
+	      if (!buffer->pos[idx].y_offset)
 	      {
-		if (!buffer->pos[idx].y_offset)
-		{
-		  buffer->pos[idx].y_advance += c->font->em_scale_y (v);
-		  buffer->pos[idx].y_offset += c->font->em_scale_y (v);
-		}
+		buffer->pos[idx].y_advance += c->font->em_scale_y (v);
+		buffer->pos[idx].y_offset += c->font->em_scale_y (v);
 	      }
 	    }
 	  }
commit 649cc3ef2773950b0b5884d9d1caf414aac888bf
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 14:04:04 2018 -0500

    [kerx] Don't disable crossKerning if kern feature is off

diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh
index 8b87e176..aabbd4c9 100644
--- a/src/hb-aat-layout-kerx-table.hh
+++ b/src/hb-aat-layout-kerx-table.hh
@@ -314,7 +314,7 @@ struct KerxSubTableFormat1
 	    v = 0;
 	  }
 
-	  if (idx < buffer->len && buffer->info[idx].mask & kern_mask)
+	  if (idx < buffer->len)
 	  {
 	    if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
 	    {
@@ -324,7 +324,7 @@ struct KerxSubTableFormat1
 		if (!buffer->pos[idx].y_offset)
 		  buffer->pos[idx].y_offset += c->font->em_scale_y (crossOffset);
 	      }
-	      else
+	      else if (buffer->info[idx].mask & kern_mask)
 	      {
 		if (!buffer->pos[idx].x_offset)
 		{
@@ -342,7 +342,7 @@ struct KerxSubTableFormat1
 		if (!buffer->pos[idx].x_offset)
 		  buffer->pos[idx].x_offset = c->font->em_scale_x (crossOffset);
 	      }
-	      else
+	      else if (buffer->info[idx].mask & kern_mask)
 	      {
 		if (!buffer->pos[idx].y_offset)
 		{
@@ -375,7 +375,8 @@ struct KerxSubTableFormat1
   {
     TRACE_APPLY (this);
 
-    if (!c->plan->requested_kerning)
+    if (!c->plan->requested_kerning &&
+	!(header.coverage & header.CrossStream))
       return false;
 
     driver_context_t dc (this, c);
diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
index d2f77132..bc4c2498 100644
--- a/src/hb-ot-shape.cc
+++ b/src/hb-ot-shape.cc
@@ -124,7 +124,7 @@ hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t &plan,
   else if (hb_aat_layout_has_positioning (face))
     plan.apply_kerx = true;
 
-  if (plan.requested_kerning && !plan.apply_kerx && !has_gpos_kern)
+  if (!plan.apply_kerx && !has_gpos_kern)
   {
     /* Apparently Apple applies kerx if GPOS kern was not applied. */
     if (hb_aat_layout_has_positioning (face))
commit 0c3b061ac244fa8a8657366e1b95523503fdf7be
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 13:58:41 2018 -0500

    [kern] Apply erlier, where GPOS/kerx are applied

diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh
index 6bbeda76..8b87e176 100644
--- a/src/hb-aat-layout-kerx-table.hh
+++ b/src/hb-aat-layout-kerx-table.hh
@@ -889,8 +889,7 @@ struct KerxTable
       if (HB_DIRECTION_IS_HORIZONTAL (c->buffer->props.direction) != st->u.header.is_horizontal ())
 	goto skip;
 
-      reverse = T::Types::extended /* TODO remove after kern application is moved earlier. */ &&
-		bool (st->u.header.coverage & st->u.header.Backwards) !=
+      reverse = bool (st->u.header.coverage & st->u.header.Backwards) !=
 		HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction);
 
       if (!c->buffer->message (c->font, "start %c%c%c%c subtable %d", HB_UNTAG (thiz()->tableTag), c->lookup_index))
diff --git a/src/hb-ot-shape-fallback.cc b/src/hb-ot-shape-fallback.cc
index cfd06e7f..bbc410ad 100644
--- a/src/hb-ot-shape-fallback.cc
+++ b/src/hb-ot-shape-fallback.cc
@@ -464,9 +464,18 @@ _hb_ot_shape_fallback_kern (const hb_ot_shape_plan_t *plan,
       !font->has_glyph_h_kerning_func () :
       !font->has_glyph_v_kerning_func ())
     return;
+
+  bool reverse = HB_DIRECTION_IS_BACKWARD (buffer->props.direction);
+
+  if (reverse)
+    buffer->reverse ();
+
   hb_ot_shape_fallback_kern_driver_t driver (font, buffer);
   OT::hb_kern_machine_t<hb_ot_shape_fallback_kern_driver_t> machine (driver);
   machine.kern (font, buffer, plan->kern_mask, false);
+
+  if (reverse)
+    buffer->reverse ();
 }
 
 
diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
index 98c29a48..d2f77132 100644
--- a/src/hb-ot-shape.cc
+++ b/src/hb-ot-shape.cc
@@ -866,10 +866,15 @@ hb_ot_position_complex (const hb_ot_shape_context_t *c)
 	break;
     }
 
+  /* XXX Clean up relationship between these. */
   if (c->plan->apply_gpos)
     c->plan->position (c->font, c->buffer);
   else if (c->plan->apply_kerx)
     hb_aat_layout_position (c->plan, c->font, c->buffer);
+  else if (c->plan->apply_kern)
+    hb_ot_layout_kern (c->plan, c->font, c->buffer);
+  else if (c->plan->fallback_kerning)
+    _hb_ot_shape_fallback_kern (c->plan, c->font, c->buffer);
 
   if (c->plan->apply_trak)
     hb_aat_layout_track (c->plan, c->font, c->buffer);
@@ -915,13 +920,6 @@ hb_ot_position (const hb_ot_shape_context_t *c)
   if (HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction))
     hb_buffer_reverse (c->buffer);
 
-  /* Visual fallback goes here. */
-
-  if (c->plan->apply_kern)
-    hb_ot_layout_kern (c->plan, c->font, c->buffer);
-  else if (c->plan->fallback_kerning)
-    _hb_ot_shape_fallback_kern (c->plan, c->font, c->buffer);
-
   _hb_buffer_deallocate_gsubgpos_vars (c->buffer);
 }
 
commit f4bad0086e40c70d66d6514f038ddda1411657c8
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 13:51:17 2018 -0500

    [kerx] Implement tupleKerning for Format1
    
    Untested.

diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh
index c1b96846..6bbeda76 100644
--- a/src/hb-aat-layout-kerx-table.hh
+++ b/src/hb-aat-layout-kerx-table.hh
@@ -295,10 +295,12 @@ struct KerxSubTableFormat1
 	    i++;
 	    break;
 	  }
+	unsigned int tuple_count = table->header.tuple_count ();
+	tuple_count = tuple_count ? tuple_count : 1;
 	for (; i; i--)
 	{
 	  unsigned int idx = stack[depth - i];
-	  int v = actions[i - 1];
+	  int v = actions[(i - 1) * tuple_count];
 
 	  /* "The end of the list is marked by an odd value..."
 	   * Ignore it. */
@@ -376,9 +378,6 @@ struct KerxSubTableFormat1
     if (!c->plan->requested_kerning)
       return false;
 
-    if (header.tuple_count ())
-      return_trace (false); /* TODO kerxTupleKern */
-
     driver_context_t dc (this, c);
 
     StateTableDriver<Types, EntryData> driver (machine, c->buffer, c->font->face);
commit 39b4ef6f18605e85c68cbcec534e137fc831dbca
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 13:48:45 2018 -0500

    [kerx] Better sanitize tupleKerning

diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh
index 8febd32d..c1b96846 100644
--- a/src/hb-aat-layout-kerx-table.hh
+++ b/src/hb-aat-layout-kerx-table.hh
@@ -53,7 +53,7 @@ kerxTupleKern (int value,
 
   unsigned int offset = value;
   const FWORD *pv = &StructAtOffset<FWORD> (base, offset);
-  if (unlikely (!pv->sanitize (&c->sanitizer))) return 0;
+  if (unlikely (!c->sanitizer.check_array (pv, tupleCount))) return 0;
   return *pv;
 }
 
commit 14772da06f9c67d0d40712369e26064e3dee2a91
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 13:40:22 2018 -0500

    [kern/kerx] Share KernTable, renamed to KerxTable

diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh
index 3a76f0fa..8febd32d 100644
--- a/src/hb-aat-layout-kerx-table.hh
+++ b/src/hb-aat-layout-kerx-table.hh
@@ -831,7 +831,7 @@ struct KerxSubTable
     return_trace (dispatch (c));
   }
 
-protected:
+  public:
   union {
   KerxSubTableHeader				header;
   KerxSubTableFormat0<KerxSubTableHeader>	format0;
@@ -840,7 +840,7 @@ protected:
   KerxSubTableFormat4<KerxSubTableHeader>	format4;
   KerxSubTableFormat6<KerxSubTableHeader>	format6;
   } u;
-public:
+  public:
   DEFINE_SIZE_MIN (12);
 };
 
@@ -849,33 +849,52 @@ public:
  * The 'kerx' Table
  */
 
-struct kerx
+template <typename T>
+struct KerxTable
 {
-  static const hb_tag_t tableTag = HB_AAT_TAG_kerx;
-  static const uint16_t minVersion = 2;
+  /* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */
+  inline const T* thiz (void) const { return static_cast<const T *> (this); }
 
-  typedef KerxSubTableHeader SubTableHeader;
-  typedef SubTableHeader::Types Types;
-  typedef KerxSubTable SubTable;
+  inline int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right) const
+  {
+    typedef typename T::SubTable SubTable;
 
-  inline bool has_data (void) const { return version != 0; }
+    int v = 0;
+    const SubTable *st = &thiz()->firstSubTable;
+    unsigned int count = thiz()->tableCount;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      if ((st->u.header.coverage & (st->u.header.Variation | st->u.header.CrossStream)) ||
+	  !st->u.header.is_horizontal ())
+        continue;
+      v += st->get_kerning (left, right);
+      st = &StructAfter<SubTable> (*st);
+    }
+    return v;
+  }
 
-  inline void apply (hb_aat_apply_context_t *c) const
+  inline void apply (AAT::hb_aat_apply_context_t *c) const
   {
+    typedef typename T::SubTable SubTable;
+
     c->set_lookup_index (0);
-    const SubTable *st = &firstSubTable;
-    unsigned int count = tableCount;
+    const SubTable *st = &thiz()->firstSubTable;
+    unsigned int count = thiz()->tableCount;
     for (unsigned int i = 0; i < count; i++)
     {
       bool reverse;
 
+      if (!T::Types::extended && (st->u.header.coverage & st->u.header.Variation))
+        goto skip;
+
       if (HB_DIRECTION_IS_HORIZONTAL (c->buffer->props.direction) != st->u.header.is_horizontal ())
 	goto skip;
 
-      reverse = bool (st->u.header.coverage & st->u.header.Backwards) !=
+      reverse = T::Types::extended /* TODO remove after kern application is moved earlier. */ &&
+		bool (st->u.header.coverage & st->u.header.Backwards) !=
 		HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction);
 
-      if (!c->buffer->message (c->font, "start %c%c%c%c subtable %d", HB_UNTAG (tableTag), c->lookup_index))
+      if (!c->buffer->message (c->font, "start %c%c%c%c subtable %d", HB_UNTAG (thiz()->tableTag), c->lookup_index))
 	goto skip;
 
       if (reverse)
@@ -890,7 +909,7 @@ struct kerx
       if (reverse)
 	c->buffer->reverse ();
 
-      (void) c->buffer->message (c->font, "end %c%c%c%c subtable %d", HB_UNTAG (tableTag), c->lookup_index);
+      (void) c->buffer->message (c->font, "end %c%c%c%c subtable %d", HB_UNTAG (thiz()->tableTag), c->lookup_index);
 
     skip:
       st = &StructAfter<SubTable> (*st);
@@ -901,22 +920,38 @@ struct kerx
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    if (unlikely (!version.sanitize (c) ||
-		  version < minVersion ||
-		  !tableCount.sanitize (c)))
+    if (unlikely (!thiz()->version.sanitize (c) ||
+		  thiz()->version < T::minVersion ||
+		  !thiz()->tableCount.sanitize (c)))
       return_trace (false);
 
-    const SubTable *st = &firstSubTable;
-    unsigned int count = tableCount;
+    typedef typename T::SubTable SubTable;
+
+    const SubTable *st = &thiz()->firstSubTable;
+    unsigned int count = thiz()->tableCount;
     for (unsigned int i = 0; i < count; i++)
     {
-      if (!st->sanitize (c))
+      if (unlikely (!st->sanitize (c)))
 	return_trace (false);
       st = &StructAfter<SubTable> (*st);
     }
 
     return_trace (true);
   }
+};
+
+struct kerx : KerxTable<kerx>
+{
+  friend struct KerxTable<kerx>;
+
+  static const hb_tag_t tableTag = HB_AAT_TAG_kerx;
+  static const uint16_t minVersion = 2;
+
+  typedef KerxSubTableHeader SubTableHeader;
+  typedef SubTableHeader::Types Types;
+  typedef KerxSubTable SubTable;
+
+  inline bool has_data (void) const { return version; }
 
   protected:
   HBUINT16	version;	/* The version number of the extended kerning table
diff --git a/src/hb-ot-kern-table.hh b/src/hb-ot-kern-table.hh
index 0976d16d..b90f7fba 100644
--- a/src/hb-ot-kern-table.hh
+++ b/src/hb-ot-kern-table.hh
@@ -158,98 +158,6 @@ struct KernSubTable
 };
 
 
-template <typename T>
-struct KernTable
-{
-  /* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */
-  inline const T* thiz (void) const { return static_cast<const T *> (this); }
-
-  inline int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right) const
-  {
-    typedef typename T::SubTable SubTable;
-
-    int v = 0;
-    const SubTable *st = &thiz()->firstSubTable;
-    unsigned int count = thiz()->tableCount;
-    for (unsigned int i = 0; i < count; i++)
-    {
-      if ((st->u.header.coverage & (st->u.header.Variation | st->u.header.CrossStream)) ||
-	  !st->u.header.is_horizontal ())
-        continue;
-      v += st->get_kerning (left, right);
-      st = &StructAfter<SubTable> (*st);
-    }
-    return v;
-  }
-
-  inline void apply (AAT::hb_aat_apply_context_t *c) const
-  {
-    typedef typename T::SubTable SubTable;
-
-    c->set_lookup_index (0);
-    const SubTable *st = &thiz()->firstSubTable;
-    unsigned int count = thiz()->tableCount;
-    for (unsigned int i = 0; i < count; i++)
-    {
-      bool reverse;
-
-      if (!T::Types::extended && (st->u.header.coverage & st->u.header.Variation))
-        goto skip;
-
-      if (HB_DIRECTION_IS_HORIZONTAL (c->buffer->props.direction) != st->u.header.is_horizontal ())
-	goto skip;
-
-      reverse = T::Types::extended /* TODO remove after kern application is moved earlier. */ &&
-		bool (st->u.header.coverage & st->u.header.Backwards) !=
-		HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction);
-
-      if (!c->buffer->message (c->font, "start %c%c%c%c subtable %d", HB_UNTAG (thiz()->tableTag), c->lookup_index))
-	goto skip;
-
-      if (reverse)
-	c->buffer->reverse ();
-
-      c->sanitizer.set_object (*st);
-
-      /* XXX Reverse-kern is probably not working yet...
-       * hb_kern_machine_t would need to know that it's reverse-kerning. */
-      st->dispatch (c);
-
-      if (reverse)
-	c->buffer->reverse ();
-
-      (void) c->buffer->message (c->font, "end %c%c%c%c subtable %d", HB_UNTAG (thiz()->tableTag), c->lookup_index);
-
-    skip:
-      st = &StructAfter<SubTable> (*st);
-      c->set_lookup_index (c->lookup_index + 1);
-    }
-  }
-
-  inline bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    if (unlikely (!thiz()->version.sanitize (c) ||
-		  thiz()->version < T::minVersion ||
-		  !thiz()->tableCount.sanitize (c)))
-      return_trace (false);
-
-    typedef typename T::SubTable SubTable;
-
-    const SubTable *st = &thiz()->firstSubTable;
-    unsigned int count = thiz()->tableCount;
-    for (unsigned int i = 0; i < count; i++)
-    {
-      if (unlikely (!st->sanitize (c)))
-	return_trace (false);
-      st = &StructAfter<SubTable> (*st);
-    }
-
-    return_trace (true);
-  }
-};
-
-
 struct KernOTSubTableHeader
 {
   static const bool apple = false;
@@ -285,9 +193,9 @@ struct KernOTSubTableHeader
   DEFINE_SIZE_STATIC (6);
 };
 
-struct KernOT : KernTable<KernOT>
+struct KernOT : AAT::KerxTable<KernOT>
 {
-  friend struct KernTable<KernOT>;
+  friend struct AAT::KerxTable<KernOT>;
 
   static const hb_tag_t tableTag = HB_OT_TAG_kern;
   static const uint16_t minVersion = 0;
@@ -340,9 +248,9 @@ struct KernAATSubTableHeader
   DEFINE_SIZE_STATIC (8);
 };
 
-struct KernAAT : KernTable<KernAAT>
+struct KernAAT : AAT::KerxTable<KernAAT>
 {
-  friend struct KernTable<KernAAT>;
+  friend struct AAT::KerxTable<KernAAT>;
 
   static const hb_tag_t tableTag = HB_OT_TAG_kern;
   static const uint32_t minVersion = 0x00010000u;
@@ -363,8 +271,7 @@ struct kern
 {
   static const hb_tag_t tableTag = HB_OT_TAG_kern;
 
-  inline bool has_data (void) const
-  { return u.version32 != 0; }
+  inline bool has_data (void) const { return u.version32; }
 
   inline int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right) const
   {
commit c038f5be6b70b8edffc701dd3e4e3cd08d14e2f0
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 13:35:06 2018 -0500

    [fallback] Minor

diff --git a/src/hb-ot-shape-fallback.cc b/src/hb-ot-shape-fallback.cc
index 3742d670..cfd06e7f 100644
--- a/src/hb-ot-shape-fallback.cc
+++ b/src/hb-ot-shape-fallback.cc
@@ -25,7 +25,7 @@
  */
 
 #include "hb-ot-shape-fallback.hh"
-#include "hb-ot-kern-table.hh"
+#include "hb-kern.hh"
 
 static unsigned int
 recategorize_combining_class (hb_codepoint_t u,
commit db6e658e8c0c4953c2f026f6a67a5d2fb4bdc204
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 13:33:23 2018 -0500

    [kern/kerx] More towards sharing KernTable

diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh
index 2bbd11aa..3a76f0fa 100644
--- a/src/hb-aat-layout-kerx-table.hh
+++ b/src/hb-aat-layout-kerx-table.hh
@@ -773,16 +773,16 @@ struct KerxSubTableHeader
 
   enum Coverage
   {
-    Vertical		= 0x80000000,	/* Set if table has vertical kerning values. */
-    CrossStream		= 0x40000000,	/* Set if table has cross-stream kerning values. */
-    Variation		= 0x20000000,	/* Set if table has variation kerning values. */
-    Backwards		= 0x10000000,	/* If clear, process the glyphs forwards, that
-					 * is, from first to last in the glyph stream.
-					 * If we, process them from last to first.
-					 * This flag only applies to state-table based
-					 * 'kerx' subtables (types 1 and 4). */
-    Reserved		= 0x0FFFFF00,	/* Reserved, set to zero. */
-    SubtableType	= 0x000000FF,	/* Subtable type. */
+    Vertical	= 0x80000000u,	/* Set if table has vertical kerning values. */
+    CrossStream	= 0x40000000u,	/* Set if table has cross-stream kerning values. */
+    Variation	= 0x20000000u,	/* Set if table has variation kerning values. */
+    Backwards	= 0x10000000u,	/* If clear, process the glyphs forwards, that
+				 * is, from first to last in the glyph stream.
+				 * If we, process them from last to first.
+				 * This flag only applies to state-table based
+				 * 'kerx' subtables (types 1 and 4). */
+    Reserved	= 0x0FFFFF00u,	/* Reserved, set to zero. */
+    SubtableType= 0x000000FFu,	/* Subtable type. */
   };
 
   inline bool sanitize (hb_sanitize_context_t *c) const
@@ -854,12 +854,16 @@ struct kerx
   static const hb_tag_t tableTag = HB_AAT_TAG_kerx;
   static const uint16_t minVersion = 2;
 
+  typedef KerxSubTableHeader SubTableHeader;
+  typedef SubTableHeader::Types Types;
+  typedef KerxSubTable SubTable;
+
   inline bool has_data (void) const { return version != 0; }
 
   inline void apply (hb_aat_apply_context_t *c) const
   {
     c->set_lookup_index (0);
-    const KerxSubTable *st = &firstSubTable;
+    const SubTable *st = &firstSubTable;
     unsigned int count = tableCount;
     for (unsigned int i = 0; i < count; i++)
     {
@@ -871,7 +875,7 @@ struct kerx
       reverse = bool (st->u.header.coverage & st->u.header.Backwards) !=
 		HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction);
 
-      if (!c->buffer->message (c->font, "start kerx subtable %d", c->lookup_index))
+      if (!c->buffer->message (c->font, "start %c%c%c%c subtable %d", HB_UNTAG (tableTag), c->lookup_index))
 	goto skip;
 
       if (reverse)
@@ -879,19 +883,17 @@ struct kerx
 
       c->sanitizer.set_object (*st);
 
-      /* XXX Reverse-kern is not working yet...
-       * hb_kern_machine_t would need to know that it's reverse-kerning.
-       * Or better yet, make it work in reverse as well, so we don't have
-       * to reverse and reverse back? */
+      /* XXX Reverse-kern is probably not working yet...
+       * hb_kern_machine_t would need to know that it's reverse-kerning. */
       st->dispatch (c);
 
       if (reverse)
 	c->buffer->reverse ();
 
-      (void) c->buffer->message (c->font, "end kerx subtable %d", c->lookup_index);
+      (void) c->buffer->message (c->font, "end %c%c%c%c subtable %d", HB_UNTAG (tableTag), c->lookup_index);
 
     skip:
-      st = &StructAfter<KerxSubTable> (*st);
+      st = &StructAfter<SubTable> (*st);
       c->set_lookup_index (c->lookup_index + 1);
     }
   }
@@ -899,16 +901,18 @@ struct kerx
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    if (!version.sanitize (c) || version < minVersion || !tableCount.sanitize (c))
+    if (unlikely (!version.sanitize (c) ||
+		  version < minVersion ||
+		  !tableCount.sanitize (c)))
       return_trace (false);
 
-    const KerxSubTable *st = &firstSubTable;
+    const SubTable *st = &firstSubTable;
     unsigned int count = tableCount;
     for (unsigned int i = 0; i < count; i++)
     {
       if (!st->sanitize (c))
 	return_trace (false);
-      st = &StructAfter<KerxSubTable> (*st);
+      st = &StructAfter<SubTable> (*st);
     }
 
     return_trace (true);
@@ -920,7 +924,7 @@ struct kerx
   HBUINT16	unused;		/* Set to 0. */
   HBUINT32	tableCount;	/* The number of subtables included in the extended kerning
 				 * table. */
-  KerxSubTable	firstSubTable;	/* Subtables. */
+  SubTable	firstSubTable;	/* Subtables. */
 /*subtableGlyphCoverageArray*/	/* Only if version >= 3. We don't use. */
 
   public:
diff --git a/src/hb-ot-kern-table.hh b/src/hb-ot-kern-table.hh
index 0102bb2c..0976d16d 100644
--- a/src/hb-ot-kern-table.hh
+++ b/src/hb-ot-kern-table.hh
@@ -191,20 +191,34 @@ struct KernTable
     unsigned int count = thiz()->tableCount;
     for (unsigned int i = 0; i < count; i++)
     {
-      if (st->u.header.coverage & st->u.header.Variation)
+      bool reverse;
+
+      if (!T::Types::extended && (st->u.header.coverage & st->u.header.Variation))
         goto skip;
 
       if (HB_DIRECTION_IS_HORIZONTAL (c->buffer->props.direction) != st->u.header.is_horizontal ())
 	goto skip;
 
-      if (!c->buffer->message (c->font, "start kern subtable %d", c->lookup_index))
+      reverse = T::Types::extended /* TODO remove after kern application is moved earlier. */ &&
+		bool (st->u.header.coverage & st->u.header.Backwards) !=
+		HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction);
+
+      if (!c->buffer->message (c->font, "start %c%c%c%c subtable %d", HB_UNTAG (thiz()->tableTag), c->lookup_index))
 	goto skip;
 
+      if (reverse)
+	c->buffer->reverse ();
+
       c->sanitizer.set_object (*st);
 
+      /* XXX Reverse-kern is probably not working yet...
+       * hb_kern_machine_t would need to know that it's reverse-kerning. */
       st->dispatch (c);
 
-      (void) c->buffer->message (c->font, "end kern subtable %d", c->lookup_index);
+      if (reverse)
+	c->buffer->reverse ();
+
+      (void) c->buffer->message (c->font, "end %c%c%c%c subtable %d", HB_UNTAG (thiz()->tableTag), c->lookup_index);
 
     skip:
       st = &StructAfter<SubTable> (*st);
@@ -247,11 +261,13 @@ struct KernOTSubTableHeader
   enum Coverage
   {
     Horizontal	= 0x01u,
-    Minimum		= 0x02u,
+    Minimum	= 0x02u,
     CrossStream	= 0x04u,
-    Override		= 0x08u,
+    Override	= 0x08u,
 
-    Variation		= 0x00u, /* Not supported. */
+    /* Not supported: */
+    Backwards	= 0x00u,
+    Variation	= 0x00u,
   };
 
   inline bool sanitize (hb_sanitize_context_t *c) const
@@ -273,15 +289,17 @@ struct KernOT : KernTable<KernOT>
 {
   friend struct KernTable<KernOT>;
 
+  static const hb_tag_t tableTag = HB_OT_TAG_kern;
+  static const uint16_t minVersion = 0;
+
   typedef KernOTSubTableHeader SubTableHeader;
+  typedef SubTableHeader::Types Types;
   typedef KernSubTable<SubTableHeader> SubTable;
 
-  static const uint16_t minVersion = 0;
-
   protected:
-  HBUINT16			version;	/* Version--0x0000u */
-  HBUINT16			tableCount;	/* Number of subtables in the kerning table. */
-  KernSubTable<SubTableHeader>	firstSubTable;	/* Subtables. */
+  HBUINT16	version;	/* Version--0x0000u */
+  HBUINT16	tableCount;	/* Number of subtables in the kerning table. */
+  SubTable	firstSubTable;	/* Subtables. */
   public:
   DEFINE_SIZE_MIN (4);
 };
@@ -297,9 +315,12 @@ struct KernAATSubTableHeader
 
   enum Coverage
   {
-    Vertical		= 0x80u,
+    Vertical	= 0x80u,
     CrossStream	= 0x40u,
-    Variation		= 0x20u,
+    Variation	= 0x20u,
+
+    /* Not supported: */
+    Backwards	= 0x00u,
   };
 
   inline bool sanitize (hb_sanitize_context_t *c) const
@@ -323,15 +344,17 @@ struct KernAAT : KernTable<KernAAT>
 {
   friend struct KernTable<KernAAT>;
 
+  static const hb_tag_t tableTag = HB_OT_TAG_kern;
+  static const uint32_t minVersion = 0x00010000u;
+
   typedef KernAATSubTableHeader SubTableHeader;
+  typedef SubTableHeader::Types Types;
   typedef KernSubTable<SubTableHeader> SubTable;
 
-  static const uint32_t minVersion = 0x00010000u;
-
   protected:
-  HBUINT32			version;	/* Version--0x00010000u */
-  HBUINT32			tableCount;	/* Number of subtables in the kerning table. */
-  KernSubTable<SubTableHeader>	firstSubTable;	/* Subtables. */
+  HBUINT32	version;	/* Version--0x00010000u */
+  HBUINT32	tableCount;	/* Number of subtables in the kerning table. */
+  SubTable	firstSubTable;	/* Subtables. */
   public:
   DEFINE_SIZE_MIN (8);
 };
commit 89ec095979bde94bd203ed2c394f6e40629e9e78
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 13:10:05 2018 -0500

    [kern] Disable Format1 and Format3 for OT-style tables

diff --git a/src/hb-ot-kern-table.hh b/src/hb-ot-kern-table.hh
index 693c4f29..0102bb2c 100644
--- a/src/hb-ot-kern-table.hh
+++ b/src/hb-ot-kern-table.hh
@@ -128,9 +128,9 @@ struct KernSubTable
     TRACE_DISPATCH (this, subtable_type);
     switch (subtable_type) {
     case 0:	return_trace (c->dispatch (u.format0));
-    case 1:	return_trace (c->dispatch (u.format1));
+    case 1:	return_trace (u.header.apple ? c->dispatch (u.format1) : c->default_return_value ());
     case 2:	return_trace (c->dispatch (u.format2));
-    case 3:	return_trace (c->dispatch (u.format3));
+    case 3:	return_trace (u.header.apple ? c->dispatch (u.format3) : c->default_return_value ());
     default:	return_trace (c->default_return_value ());
     }
   }
@@ -238,6 +238,7 @@ struct KernTable
 
 struct KernOTSubTableHeader
 {
+  static const bool apple = false;
   typedef AAT::ObsoleteTypes Types;
 
   inline unsigned int tuple_count (void) const { return 0; }
@@ -288,6 +289,7 @@ struct KernOT : KernTable<KernOT>
 
 struct KernAATSubTableHeader
 {
+  static const bool apple = true;
   typedef AAT::ObsoleteTypes Types;
 
   inline unsigned int tuple_count (void) const { return 0; }
commit ab57bcae0fd4505c80bb4ccdef6838bb2805ce79
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 13:04:21 2018 -0500

    [kern] Minor

diff --git a/src/hb-ot-kern-table.hh b/src/hb-ot-kern-table.hh
index 5046a0da..693c4f29 100644
--- a/src/hb-ot-kern-table.hh
+++ b/src/hb-ot-kern-table.hh
@@ -166,7 +166,7 @@ struct KernTable
 
   inline int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right) const
   {
-    typedef KernSubTable<typename T::SubTableHeader> SubTable;
+    typedef typename T::SubTable SubTable;
 
     int v = 0;
     const SubTable *st = &thiz()->firstSubTable;
@@ -184,7 +184,7 @@ struct KernTable
 
   inline void apply (AAT::hb_aat_apply_context_t *c) const
   {
-    typedef KernSubTable<typename T::SubTableHeader> SubTable;
+    typedef typename T::SubTable SubTable;
 
     c->set_lookup_index (0);
     const SubTable *st = &thiz()->firstSubTable;
@@ -220,7 +220,7 @@ struct KernTable
 		  !thiz()->tableCount.sanitize (c)))
       return_trace (false);
 
-    typedef KernSubTable<typename T::SubTableHeader> SubTable;
+    typedef typename T::SubTable SubTable;
 
     const SubTable *st = &thiz()->firstSubTable;
     unsigned int count = thiz()->tableCount;
@@ -273,6 +273,7 @@ struct KernOT : KernTable<KernOT>
   friend struct KernTable<KernOT>;
 
   typedef KernOTSubTableHeader SubTableHeader;
+  typedef KernSubTable<SubTableHeader> SubTable;
 
   static const uint16_t minVersion = 0;
 
@@ -321,6 +322,7 @@ struct KernAAT : KernTable<KernAAT>
   friend struct KernTable<KernAAT>;
 
   typedef KernAATSubTableHeader SubTableHeader;
+  typedef KernSubTable<SubTableHeader> SubTable;
 
   static const uint32_t minVersion = 0x00010000u;
 
commit 30af5b4a4c2071599dc87bc092a7329befcc45cc
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 12:57:10 2018 -0500

    [kern] Move code

diff --git a/src/hb-ot-kern-table.hh b/src/hb-ot-kern-table.hh
index 461a2448..5046a0da 100644
--- a/src/hb-ot-kern-table.hh
+++ b/src/hb-ot-kern-table.hh
@@ -235,43 +235,46 @@ struct KernTable
   }
 };
 
-struct KernOT : KernTable<KernOT>
+
+struct KernOTSubTableHeader
 {
-  friend struct KernTable<KernOT>;
+  typedef AAT::ObsoleteTypes Types;
 
-  static const uint16_t minVersion = 0;
+  inline unsigned int tuple_count (void) const { return 0; }
+  inline bool is_horizontal (void) const { return (coverage & Horizontal); }
 
-  struct SubTableHeader
+  enum Coverage
   {
-    typedef AAT::ObsoleteTypes Types;
+    Horizontal	= 0x01u,
+    Minimum		= 0x02u,
+    CrossStream	= 0x04u,
+    Override		= 0x08u,
 
-    inline unsigned int tuple_count (void) const { return 0; }
-    inline bool is_horizontal (void) const { return (coverage & Horizontal); }
+    Variation		= 0x00u, /* Not supported. */
+  };
 
-    enum Coverage
-    {
-      Horizontal	= 0x01u,
-      Minimum		= 0x02u,
-      CrossStream	= 0x04u,
-      Override		= 0x08u,
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
 
-      Variation		= 0x00u, /* Not supported. */
-    };
+  public:
+  HBUINT16	versionZ;	/* Unused. */
+  HBUINT16	length;		/* Length of the subtable (including this header). */
+  HBUINT8	format;		/* Subtable format. */
+  HBUINT8	coverage;	/* Coverage bits. */
+  public:
+  DEFINE_SIZE_STATIC (6);
+};
 
-    inline bool sanitize (hb_sanitize_context_t *c) const
-    {
-      TRACE_SANITIZE (this);
-      return_trace (c->check_struct (this));
-    }
+struct KernOT : KernTable<KernOT>
+{
+  friend struct KernTable<KernOT>;
 
-    public:
-    HBUINT16	versionZ;	/* Unused. */
-    HBUINT16	length;		/* Length of the subtable (including this header). */
-    HBUINT8	format;		/* Subtable format. */
-    HBUINT8	coverage;	/* Coverage bits. */
-    public:
-    DEFINE_SIZE_STATIC (6);
-  };
+  typedef KernOTSubTableHeader SubTableHeader;
+
+  static const uint16_t minVersion = 0;
 
   protected:
   HBUINT16			version;	/* Version--0x0000u */
@@ -281,42 +284,45 @@ struct KernOT : KernTable<KernOT>
   DEFINE_SIZE_MIN (4);
 };
 
-struct KernAAT : KernTable<KernAAT>
+
+struct KernAATSubTableHeader
 {
-  friend struct KernTable<KernAAT>;
+  typedef AAT::ObsoleteTypes Types;
 
-  static const uint32_t minVersion = 0x00010000u;
+  inline unsigned int tuple_count (void) const { return 0; }
+  inline bool is_horizontal (void) const { return !(coverage & Vertical); }
 
-  struct SubTableHeader
+  enum Coverage
   {
-    typedef AAT::ObsoleteTypes Types;
+    Vertical		= 0x80u,
+    CrossStream	= 0x40u,
+    Variation		= 0x20u,
+  };
 
-    inline unsigned int tuple_count (void) const { return 0; }
-    inline bool is_horizontal (void) const { return !(coverage & Vertical); }
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
 
-    enum Coverage
-    {
-      Vertical		= 0x80u,
-      CrossStream	= 0x40u,
-      Variation		= 0x20u,
-    };
+  public:
+  HBUINT32	length;		/* Length of the subtable (including this header). */
+  HBUINT8	coverage;	/* Coverage bits. */
+  HBUINT8	format;		/* Subtable format. */
+  HBUINT16	tupleIndex;	/* The tuple index (used for variations fonts).
+			       * This value specifies which tuple this subtable covers.
+			       * Note: We don't implement. */
+  public:
+  DEFINE_SIZE_STATIC (8);
+};
 
-    inline bool sanitize (hb_sanitize_context_t *c) const
-    {
-      TRACE_SANITIZE (this);
-      return_trace (c->check_struct (this));
-    }
+struct KernAAT : KernTable<KernAAT>
+{
+  friend struct KernTable<KernAAT>;
 
-    public:
-    HBUINT32	length;		/* Length of the subtable (including this header). */
-    HBUINT8	coverage;	/* Coverage bits. */
-    HBUINT8	format;		/* Subtable format. */
-    HBUINT16	tupleIndex;	/* The tuple index (used for variations fonts).
-				 * This value specifies which tuple this subtable covers.
-				 * Note: We don't implement. */
-    public:
-    DEFINE_SIZE_STATIC (8);
-  };
+  typedef KernAATSubTableHeader SubTableHeader;
+
+  static const uint32_t minVersion = 0x00010000u;
 
   protected:
   HBUINT32			version;	/* Version--0x00010000u */
commit 1ff300464a1075b8cd5311970afbbcf4bb3b6f3d
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 12:51:49 2018 -0500

    [kern] Massage more

diff --git a/src/hb-ot-kern-table.hh b/src/hb-ot-kern-table.hh
index 53845119..461a2448 100644
--- a/src/hb-ot-kern-table.hh
+++ b/src/hb-ot-kern-table.hh
@@ -169,7 +169,7 @@ struct KernTable
     typedef KernSubTable<typename T::SubTableHeader> SubTable;
 
     int v = 0;
-    const SubTable *st = CastP<SubTable> (&thiz()->dataZ);
+    const SubTable *st = &thiz()->firstSubTable;
     unsigned int count = thiz()->tableCount;
     for (unsigned int i = 0; i < count; i++)
     {
@@ -187,9 +187,8 @@ struct KernTable
     typedef KernSubTable<typename T::SubTableHeader> SubTable;
 
     c->set_lookup_index (0);
-    const SubTable *st = CastP<SubTable> (&thiz()->dataZ);
+    const SubTable *st = &thiz()->firstSubTable;
     unsigned int count = thiz()->tableCount;
-    st = CastP<SubTable> (&thiz()->dataZ);
     for (unsigned int i = 0; i < count; i++)
     {
       if (st->u.header.coverage & st->u.header.Variation)
@@ -223,7 +222,7 @@ struct KernTable
 
     typedef KernSubTable<typename T::SubTableHeader> SubTable;
 
-    const SubTable *st = CastP<SubTable> (&thiz()->dataZ);
+    const SubTable *st = &thiz()->firstSubTable;
     unsigned int count = thiz()->tableCount;
     for (unsigned int i = 0; i < count; i++)
     {
@@ -277,9 +276,9 @@ struct KernOT : KernTable<KernOT>
   protected:
   HBUINT16			version;	/* Version--0x0000u */
   HBUINT16			tableCount;	/* Number of subtables in the kerning table. */
-  UnsizedArrayOf<HBUINT8>	dataZ;
+  KernSubTable<SubTableHeader>	firstSubTable;	/* Subtables. */
   public:
-  DEFINE_SIZE_ARRAY (4, dataZ);
+  DEFINE_SIZE_MIN (4);
 };
 
 struct KernAAT : KernTable<KernAAT>
@@ -322,9 +321,9 @@ struct KernAAT : KernTable<KernAAT>
   protected:
   HBUINT32			version;	/* Version--0x00010000u */
   HBUINT32			tableCount;	/* Number of subtables in the kerning table. */
-  UnsizedArrayOf<HBUINT8>	dataZ;
+  KernSubTable<SubTableHeader>	firstSubTable;	/* Subtables. */
   public:
-  DEFINE_SIZE_ARRAY (8, dataZ);
+  DEFINE_SIZE_MIN (8);
 };
 
 struct kern
commit 8e9f6cd0fddd572e048487aae3141d3dbb1b99cb
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 12:49:20 2018 -0500

    [kerx] More minor

diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh
index 63638026..2bbd11aa 100644
--- a/src/hb-aat-layout-kerx-table.hh
+++ b/src/hb-aat-layout-kerx-table.hh
@@ -859,7 +859,7 @@ struct kerx
   inline void apply (hb_aat_apply_context_t *c) const
   {
     c->set_lookup_index (0);
-    const KerxSubTable *st = &firstTable;
+    const KerxSubTable *st = &firstSubTable;
     unsigned int count = tableCount;
     for (unsigned int i = 0; i < count; i++)
     {
@@ -902,7 +902,7 @@ struct kerx
     if (!version.sanitize (c) || version < minVersion || !tableCount.sanitize (c))
       return_trace (false);
 
-    const KerxSubTable *st = &firstTable;
+    const KerxSubTable *st = &firstSubTable;
     unsigned int count = tableCount;
     for (unsigned int i = 0; i < count; i++)
     {
@@ -920,8 +920,7 @@ struct kerx
   HBUINT16	unused;		/* Set to 0. */
   HBUINT32	tableCount;	/* The number of subtables included in the extended kerning
 				 * table. */
-  KerxSubTable	firstTable;	/* Subtables. */
-  UnsizedArrayOf<HBUINT8>	dataZ;
+  KerxSubTable	firstSubTable;	/* Subtables. */
 /*subtableGlyphCoverageArray*/	/* Only if version >= 3. We don't use. */
 
   public:
commit f8c3df7d4a685bb86a1c15a5ef95485e8ef30305
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 12:48:06 2018 -0500

    [kern/kerx] Minor

diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh
index 9a221bc5..63638026 100644
--- a/src/hb-aat-layout-kerx-table.hh
+++ b/src/hb-aat-layout-kerx-table.hh
@@ -852,6 +852,7 @@ public:
 struct kerx
 {
   static const hb_tag_t tableTag = HB_AAT_TAG_kerx;
+  static const uint16_t minVersion = 2;
 
   inline bool has_data (void) const { return version != 0; }
 
@@ -898,8 +899,7 @@ struct kerx
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    if (!version.sanitize (c) || version < 2 ||
-	!tableCount.sanitize (c))
+    if (!version.sanitize (c) || version < minVersion || !tableCount.sanitize (c))
       return_trace (false);
 
     const KerxSubTable *st = &firstTable;
@@ -921,6 +921,7 @@ struct kerx
   HBUINT32	tableCount;	/* The number of subtables included in the extended kerning
 				 * table. */
   KerxSubTable	firstTable;	/* Subtables. */
+  UnsizedArrayOf<HBUINT8>	dataZ;
 /*subtableGlyphCoverageArray*/	/* Only if version >= 3. We don't use. */
 
   public:
diff --git a/src/hb-ot-kern-table.hh b/src/hb-ot-kern-table.hh
index 89da9e36..53845119 100644
--- a/src/hb-ot-kern-table.hh
+++ b/src/hb-ot-kern-table.hh
@@ -170,7 +170,7 @@ struct KernTable
 
     int v = 0;
     const SubTable *st = CastP<SubTable> (&thiz()->dataZ);
-    unsigned int count = thiz()->nTables;
+    unsigned int count = thiz()->tableCount;
     for (unsigned int i = 0; i < count; i++)
     {
       if ((st->u.header.coverage & (st->u.header.Variation | st->u.header.CrossStream)) ||
@@ -188,7 +188,7 @@ struct KernTable
 
     c->set_lookup_index (0);
     const SubTable *st = CastP<SubTable> (&thiz()->dataZ);
-    unsigned int count = thiz()->nTables;
+    unsigned int count = thiz()->tableCount;
     st = CastP<SubTable> (&thiz()->dataZ);
     for (unsigned int i = 0; i < count; i++)
     {
@@ -216,14 +216,15 @@ struct KernTable
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    if (unlikely (!c->check_struct (thiz()) ||
-		  thiz()->version != T::VERSION))
+    if (unlikely (!thiz()->version.sanitize (c) ||
+		  thiz()->version < T::minVersion ||
+		  !thiz()->tableCount.sanitize (c)))
       return_trace (false);
 
     typedef KernSubTable<typename T::SubTableHeader> SubTable;
 
     const SubTable *st = CastP<SubTable> (&thiz()->dataZ);
-    unsigned int count = thiz()->nTables;
+    unsigned int count = thiz()->tableCount;
     for (unsigned int i = 0; i < count; i++)
     {
       if (unlikely (!st->sanitize (c)))
@@ -239,7 +240,7 @@ struct KernOT : KernTable<KernOT>
 {
   friend struct KernTable<KernOT>;
 
-  static const uint16_t VERSION = 0x0000u;
+  static const uint16_t minVersion = 0;
 
   struct SubTableHeader
   {
@@ -275,7 +276,7 @@ struct KernOT : KernTable<KernOT>
 
   protected:
   HBUINT16			version;	/* Version--0x0000u */
-  HBUINT16			nTables;	/* Number of subtables in the kerning table. */
+  HBUINT16			tableCount;	/* Number of subtables in the kerning table. */
   UnsizedArrayOf<HBUINT8>	dataZ;
   public:
   DEFINE_SIZE_ARRAY (4, dataZ);
@@ -285,7 +286,7 @@ struct KernAAT : KernTable<KernAAT>
 {
   friend struct KernTable<KernAAT>;
 
-  static const uint32_t VERSION = 0x00010000u;
+  static const uint32_t minVersion = 0x00010000u;
 
   struct SubTableHeader
   {
@@ -320,7 +321,7 @@ struct KernAAT : KernTable<KernAAT>
 
   protected:
   HBUINT32			version;	/* Version--0x00010000u */
-  HBUINT32			nTables;	/* Number of subtables in the kerning table. */
+  HBUINT32			tableCount;	/* Number of subtables in the kerning table. */
   UnsizedArrayOf<HBUINT8>	dataZ;
   public:
   DEFINE_SIZE_ARRAY (8, dataZ);
commit f5e0a63a22f91720a997f5070b84e982e57de661
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 12:32:39 2018 -0500

    [kern/kerx] Towards sharing KernTable

diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh
index a395e76a..9a221bc5 100644
--- a/src/hb-aat-layout-kerx-table.hh
+++ b/src/hb-aat-layout-kerx-table.hh
@@ -769,6 +769,7 @@ struct KerxSubTableHeader
   typedef ExtendedTypes Types;
 
   inline unsigned int tuple_count (void) const { return tupleCount; }
+  inline bool is_horizontal (void) const { return !(coverage & Vertical); }
 
   enum Coverage
   {
@@ -863,8 +864,7 @@ struct kerx
     {
       bool reverse;
 
-      if (HB_DIRECTION_IS_VERTICAL (c->buffer->props.direction) !=
-	  bool (st->u.header.coverage & st->u.header.Vertical))
+      if (HB_DIRECTION_IS_HORIZONTAL (c->buffer->props.direction) != st->u.header.is_horizontal ())
 	goto skip;
 
       reverse = bool (st->u.header.coverage & st->u.header.Backwards) !=
diff --git a/src/hb-ot-kern-table.hh b/src/hb-ot-kern-table.hh
index f8b1aa7a..89da9e36 100644
--- a/src/hb-ot-kern-table.hh
+++ b/src/hb-ot-kern-table.hh
@@ -173,9 +173,8 @@ struct KernTable
     unsigned int count = thiz()->nTables;
     for (unsigned int i = 0; i < count; i++)
     {
-      if ((st->u.header.coverage &
-	   (st->u.header.Variation | st->u.header.CrossStream | st->u.header.Direction)) !=
-	  st->u.header.DirectionHorizontal)
+      if ((st->u.header.coverage & (st->u.header.Variation | st->u.header.CrossStream)) ||
+	  !st->u.header.is_horizontal ())
         continue;
       v += st->get_kerning (left, right);
       st = &StructAfter<SubTable> (*st);
@@ -196,8 +195,7 @@ struct KernTable
       if (st->u.header.coverage & st->u.header.Variation)
         goto skip;
 
-      if (HB_DIRECTION_IS_HORIZONTAL (c->buffer->props.direction) !=
-	  ((st->u.header.coverage & st->u.header.Direction) == st->u.header.DirectionHorizontal))
+      if (HB_DIRECTION_IS_HORIZONTAL (c->buffer->props.direction) != st->u.header.is_horizontal ())
 	goto skip;
 
       if (!c->buffer->message (c->font, "start kern subtable %d", c->lookup_index))
@@ -248,18 +246,16 @@ struct KernOT : KernTable<KernOT>
     typedef AAT::ObsoleteTypes Types;
 
     inline unsigned int tuple_count (void) const { return 0; }
-
+    inline bool is_horizontal (void) const { return (coverage & Horizontal); }
 
     enum Coverage
     {
-      Direction		= 0x01u,
+      Horizontal	= 0x01u,
       Minimum		= 0x02u,
       CrossStream	= 0x04u,
       Override		= 0x08u,
 
       Variation		= 0x00u, /* Not supported. */
-
-      DirectionHorizontal= 0x01u
     };
 
     inline bool sanitize (hb_sanitize_context_t *c) const
@@ -296,14 +292,13 @@ struct KernAAT : KernTable<KernAAT>
     typedef AAT::ObsoleteTypes Types;
 
     inline unsigned int tuple_count (void) const { return 0; }
+    inline bool is_horizontal (void) const { return !(coverage & Vertical); }
 
     enum Coverage
     {
-      Direction		= 0x80u,
+      Vertical		= 0x80u,
       CrossStream	= 0x40u,
       Variation		= 0x20u,
-
-      DirectionHorizontal= 0x00u
     };
 
     inline bool sanitize (hb_sanitize_context_t *c) const
commit 330508497d301c0ba5d5fb5d0900b62c191aabb5
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 12:27:44 2018 -0500

    [kern/kerx] Minor

diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh
index d55d5f7a..a395e76a 100644
--- a/src/hb-aat-layout-kerx-table.hh
+++ b/src/hb-aat-layout-kerx-table.hh
@@ -768,7 +768,7 @@ struct KerxSubTableHeader
 {
   typedef ExtendedTypes Types;
 
-  unsigned int tuple_count (void) const { return tupleCount; }
+  inline unsigned int tuple_count (void) const { return tupleCount; }
 
   enum Coverage
   {
diff --git a/src/hb-ot-kern-table.hh b/src/hb-ot-kern-table.hh
index 27829d84..f8b1aa7a 100644
--- a/src/hb-ot-kern-table.hh
+++ b/src/hb-ot-kern-table.hh
@@ -247,7 +247,8 @@ struct KernOT : KernTable<KernOT>
   {
     typedef AAT::ObsoleteTypes Types;
 
-    unsigned int tuple_count (void) const { return 0; }
+    inline unsigned int tuple_count (void) const { return 0; }
+
 
     enum Coverage
     {
@@ -294,7 +295,7 @@ struct KernAAT : KernTable<KernAAT>
   {
     typedef AAT::ObsoleteTypes Types;
 
-    unsigned int tuple_count (void) const { return 0; }
+    inline unsigned int tuple_count (void) const { return 0; }
 
     enum Coverage
     {
commit 1a5ef8490034f4bd8965a3c71d34a5930ebe11b7
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 12:19:52 2018 -0500

    [kern/kerx] Share Format2
    
    This, enables Format2 for kern table, which was disabled before.

diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh
index 41841255..d55d5f7a 100644
--- a/src/hb-aat-layout-kerx-table.hh
+++ b/src/hb-aat-layout-kerx-table.hh
@@ -460,6 +460,12 @@ struct KerxSubTableFormat2
 			  c->check_range (this, array)));
   }
 
+  /* Note:
+   * OT kern table specifies ClassTable as having 16-bit entries, whereas
+   * AAT kern table specifies them as having 8bit entries.
+   * I've not seen any fonts with this format in kern table.
+   * We follow AAT. */
+
   protected:
   KernSubTableHeader	header;
   HBUINT		rowWidth;	/* The width, in bytes, of a row in the table. */
diff --git a/src/hb-ot-kern-table.hh b/src/hb-ot-kern-table.hh
index 870d8001..27829d84 100644
--- a/src/hb-ot-kern-table.hh
+++ b/src/hb-ot-kern-table.hh
@@ -42,77 +42,6 @@ namespace OT {
 
 
 template <typename KernSubTableHeader>
-struct KernSubTableFormat2
-{
-  inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right,
-			  AAT::hb_aat_apply_context_t *c) const
-  {
-    /* Disabled until we find a font to test this.  Note that OT vs AAT specify
-     * different ClassTable.  OT's has 16bit entries, while AAT has 8bit entries.
-     * I've not seen any in the wild. */
-    return 0;
-    unsigned int l = (this+leftClassTable).get_class (left, 0);
-    unsigned int r = (this+rightClassTable).get_class (right, 0);
-    unsigned int offset = l + r;
-    const FWORD *v = &StructAtOffset<FWORD> (&(this+array), offset);
-    if (unlikely (!v->sanitize (&c->sanitizer))) return 0;
-    return *v;
-  }
-
-  inline bool apply (AAT::hb_aat_apply_context_t *c) const
-  {
-    TRACE_APPLY (this);
-
-    if (!c->plan->requested_kerning)
-      return false;
-
-    if (header.coverage & header.CrossStream)
-      return false;
-
-    accelerator_t accel (*this, c);
-    hb_kern_machine_t<accelerator_t> machine (accel);
-    machine.kern (c->font, c->buffer, c->plan->kern_mask);
-
-    return_trace (true);
-  }
-
-  struct accelerator_t
-  {
-    const KernSubTableFormat2 &table;
-    AAT::hb_aat_apply_context_t *c;
-
-    inline accelerator_t (const KernSubTableFormat2 &table_,
-			  AAT::hb_aat_apply_context_t *c_) :
-			    table (table_), c (c_) {}
-
-    inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
-    { return table.get_kerning (left, right, c); }
-  };
-
-  inline bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (true); /* Disabled.  See above. */
-    return_trace (c->check_struct (this) &&
-		  leftClassTable.sanitize (c, this) &&
-		  rightClassTable.sanitize (c, this) &&
-		  array.sanitize (c, this));
-  }
-
-  protected:
-  KernSubTableHeader		header;
-  HBUINT16			rowWidth;	/* The width, in bytes, of a row in the table. */
-  OffsetTo<AAT::ClassTable>	leftClassTable;	/* Offset from beginning of this subtable to
-						 * left-hand class table. */
-  OffsetTo<AAT::ClassTable>	rightClassTable;/* Offset from beginning of this subtable to
-						 * right-hand class table. */
-  OffsetTo<FWORD>		array;		/* Offset from beginning of this subtable to
-						 * the start of the kerning array. */
-  public:
-  DEFINE_SIZE_MIN (KernSubTableHeader::static_size + 8);
-};
-
-template <typename KernSubTableHeader>
 struct KernSubTableFormat3
 {
   inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
@@ -221,7 +150,7 @@ struct KernSubTable
   KernSubTableHeader				header;
   AAT::KerxSubTableFormat0<KernSubTableHeader>	format0;
   AAT::KerxSubTableFormat1<KernSubTableHeader>	format1;
-  KernSubTableFormat2<KernSubTableHeader>	format2;
+  AAT::KerxSubTableFormat2<KernSubTableHeader>	format2;
   KernSubTableFormat3<KernSubTableHeader>	format3;
   } u;
   public:
commit 8faec4e33486616fdc0d690ad80d4a38a73c8182
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 12:16:38 2018 -0500

    [kerx] Towards merging Format2

diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh
index 9e1105de..41841255 100644
--- a/src/hb-aat-layout-kerx-table.hh
+++ b/src/hb-aat-layout-kerx-table.hh
@@ -406,12 +406,15 @@ struct KerxSubTableFormat1
 template <typename KernSubTableHeader>
 struct KerxSubTableFormat2
 {
+  typedef typename KernSubTableHeader::Types Types;
+  typedef typename Types::HBUINT HBUINT;
+
   inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right,
 			  hb_aat_apply_context_t *c) const
   {
     unsigned int num_glyphs = c->sanitizer.get_num_glyphs ();
-    unsigned int l = (this+leftClassTable).get_value_or_null (left, num_glyphs);
-    unsigned int r = (this+rightClassTable).get_value_or_null (right, num_glyphs);
+    unsigned int l = (this+leftClassTable).get_class (left, num_glyphs, 0);
+    unsigned int r = (this+rightClassTable).get_class (right, num_glyphs, 0);
     unsigned int offset = l + r;
     const FWORD *v = &StructAtOffset<FWORD> (&(this+array), offset);
     if (unlikely (!v->sanitize (&c->sanitizer))) return 0;
@@ -459,18 +462,18 @@ struct KerxSubTableFormat2
 
   protected:
   KernSubTableHeader	header;
-  HBUINT32		rowWidth;	/* The width, in bytes, of a row in the table. */
-  LOffsetTo<Lookup<HBUINT16>, false>
+  HBUINT		rowWidth;	/* The width, in bytes, of a row in the table. */
+  OffsetTo<typename Types::ClassType, HBUINT, false>
 			leftClassTable;	/* Offset from beginning of this subtable to
 					 * left-hand class table. */
-  LOffsetTo<Lookup<HBUINT16>, false>
+  OffsetTo<typename Types::ClassType, HBUINT, false>
 			rightClassTable;/* Offset from beginning of this subtable to
 					 * right-hand class table. */
-  LOffsetTo<UnsizedArrayOf<FWORD>, false>
+  OffsetTo<UnsizedArrayOf<FWORD>, HBUINT, false>
 			 array;		/* Offset from beginning of this subtable to
 					 * the start of the kerning array. */
   public:
-  DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 16);
+  DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 4 * sizeof (HBUINT));
 };
 
 template <typename KernSubTableHeader>
commit d5c0ca210fef315fd039e5b1825a865f36606a3f
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 12:08:44 2018 -0500

    [aat] Minor

diff --git a/src/hb-aat-layout-common.hh b/src/hb-aat-layout-common.hh
index cede80c8..34c61e93 100644
--- a/src/hb-aat-layout-common.hh
+++ b/src/hb-aat-layout-common.hh
@@ -436,7 +436,7 @@ struct StateTable
   inline unsigned int get_class (hb_codepoint_t glyph_id, unsigned int num_glyphs) const
   {
     if (unlikely (glyph_id == DELETED_GLYPH)) return CLASS_DELETED_GLYPH;
-    return (this+classTable).get_class (glyph_id, num_glyphs);
+    return (this+classTable).get_class (glyph_id, num_glyphs, 1);
   }
 
   inline const Entry<Extra> *get_entries () const
@@ -528,7 +528,7 @@ struct StateTable
 
 struct ClassTable
 {
-  inline unsigned int get_class (hb_codepoint_t glyph_id, unsigned int outOfRange=0) const
+  inline unsigned int get_class (hb_codepoint_t glyph_id, unsigned int outOfRange) const
   {
     unsigned int i = glyph_id - firstGlyph;
     return i >= classArray.len ? outOfRange : classArray.arrayZ[i];
@@ -553,9 +553,11 @@ struct ObsoleteTypes
   typedef HBUINT8 HBUSHORT;
   struct ClassType : ClassTable
   {
-    inline unsigned int get_class (hb_codepoint_t glyph_id, unsigned int num_glyphs HB_UNUSED) const
+    inline unsigned int get_class (hb_codepoint_t glyph_id,
+				   unsigned int num_glyphs HB_UNUSED,
+				   unsigned int outOfRange) const
     {
-      return ClassTable::get_class (glyph_id, 1);
+      return ClassTable::get_class (glyph_id, outOfRange);
     }
   };
   template <typename T>
@@ -580,10 +582,12 @@ struct ExtendedTypes
   typedef HBUINT16 HBUSHORT;
   struct ClassType : Lookup<HBUINT16>
   {
-    inline unsigned int get_class (hb_codepoint_t glyph_id, unsigned int num_glyphs) const
+    inline unsigned int get_class (hb_codepoint_t glyph_id,
+				   unsigned int num_glyphs,
+				   unsigned int outOfRange) const
     {
       const HBUINT16 *v = get_value (glyph_id, num_glyphs);
-      return v ? *v : 1;
+      return v ? *v : outOfRange;
     }
   };
   template <typename T>
diff --git a/src/hb-ot-kern-table.hh b/src/hb-ot-kern-table.hh
index 3f771d2d..870d8001 100644
--- a/src/hb-ot-kern-table.hh
+++ b/src/hb-ot-kern-table.hh
@@ -51,8 +51,8 @@ struct KernSubTableFormat2
      * different ClassTable.  OT's has 16bit entries, while AAT has 8bit entries.
      * I've not seen any in the wild. */
     return 0;
-    unsigned int l = (this+leftClassTable).get_class (left);
-    unsigned int r = (this+rightClassTable).get_class (right);
+    unsigned int l = (this+leftClassTable).get_class (left, 0);
+    unsigned int r = (this+rightClassTable).get_class (right, 0);
     unsigned int offset = l + r;
     const FWORD *v = &StructAtOffset<FWORD> (&(this+array), offset);
     if (unlikely (!v->sanitize (&c->sanitizer))) return 0;
commit e72e041c3cda164b2ffb02d770b35d0d70954818
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 11:56:36 2018 -0500

    [kerx] Rename

diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh
index 6469e5cf..9e1105de 100644
--- a/src/hb-aat-layout-kerx-table.hh
+++ b/src/hb-aat-layout-kerx-table.hh
@@ -789,7 +789,7 @@ struct KerxSubTableHeader
   DEFINE_SIZE_STATIC (12);
 };
 
-struct KerxTable
+struct KerxSubTable
 {
   friend struct kerx;
 
@@ -848,17 +848,17 @@ struct kerx
   inline void apply (hb_aat_apply_context_t *c) const
   {
     c->set_lookup_index (0);
-    const KerxTable *table = &firstTable;
+    const KerxSubTable *st = &firstTable;
     unsigned int count = tableCount;
     for (unsigned int i = 0; i < count; i++)
     {
       bool reverse;
 
       if (HB_DIRECTION_IS_VERTICAL (c->buffer->props.direction) !=
-	  bool (table->u.header.coverage & table->u.header.Vertical))
+	  bool (st->u.header.coverage & st->u.header.Vertical))
 	goto skip;
 
-      reverse = bool (table->u.header.coverage & table->u.header.Backwards) !=
+      reverse = bool (st->u.header.coverage & st->u.header.Backwards) !=
 		HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction);
 
       if (!c->buffer->message (c->font, "start kerx subtable %d", c->lookup_index))
@@ -867,13 +867,13 @@ struct kerx
       if (reverse)
 	c->buffer->reverse ();
 
-      c->sanitizer.set_object (*table);
+      c->sanitizer.set_object (*st);
 
       /* XXX Reverse-kern is not working yet...
        * hb_kern_machine_t would need to know that it's reverse-kerning.
        * Or better yet, make it work in reverse as well, so we don't have
        * to reverse and reverse back? */
-      table->dispatch (c);
+      st->dispatch (c);
 
       if (reverse)
 	c->buffer->reverse ();
@@ -881,7 +881,7 @@ struct kerx
       (void) c->buffer->message (c->font, "end kerx subtable %d", c->lookup_index);
 
     skip:
-      table = &StructAfter<KerxTable> (*table);
+      st = &StructAfter<KerxSubTable> (*st);
       c->set_lookup_index (c->lookup_index + 1);
     }
   }
@@ -893,13 +893,13 @@ struct kerx
 	!tableCount.sanitize (c))
       return_trace (false);
 
-    const KerxTable *table = &firstTable;
+    const KerxSubTable *st = &firstTable;
     unsigned int count = tableCount;
     for (unsigned int i = 0; i < count; i++)
     {
-      if (!table->sanitize (c))
+      if (!st->sanitize (c))
 	return_trace (false);
-      table = &StructAfter<KerxTable> (*table);
+      st = &StructAfter<KerxSubTable> (*st);
     }
 
     return_trace (true);
@@ -911,7 +911,7 @@ struct kerx
   HBUINT16	unused;		/* Set to 0. */
   HBUINT32	tableCount;	/* The number of subtables included in the extended kerning
 				 * table. */
-  KerxTable	firstTable;	/* Subtables. */
+  KerxSubTable	firstTable;	/* Subtables. */
 /*subtableGlyphCoverageArray*/	/* Only if version >= 3. We don't use. */
 
   public:
commit 241ba7da518adee334fff105ae19dfb051868a57
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 11:51:40 2018 -0500

    [morx/kerx] Rename types

diff --git a/src/hb-aat-layout-common.hh b/src/hb-aat-layout-common.hh
index 539941d8..cede80c8 100644
--- a/src/hb-aat-layout-common.hh
+++ b/src/hb-aat-layout-common.hh
@@ -546,7 +546,7 @@ struct ClassTable
   DEFINE_SIZE_ARRAY (4, classArray);
 };
 
-struct MortTypes
+struct ObsoleteTypes
 {
   static const bool extended = false;
   typedef HBUINT16 HBUINT;
@@ -573,7 +573,7 @@ struct MortTypes
     return offsetToIndex (2 * offset, base, array);
   }
 };
-struct MorxTypes
+struct ExtendedTypes
 {
   static const bool extended = true;
   typedef HBUINT32 HBUINT;
diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh
index 2b26d496..6469e5cf 100644
--- a/src/hb-aat-layout-kerx-table.hh
+++ b/src/hb-aat-layout-kerx-table.hh
@@ -476,6 +476,8 @@ struct KerxSubTableFormat2
 template <typename KernSubTableHeader>
 struct KerxSubTableFormat4
 {
+  typedef ExtendedTypes Types;
+
   struct EntryData
   {
     HBUINT16	ankrActionIndex;/* Either 0xFFFF (for no action) or the index of
@@ -512,12 +514,12 @@ struct KerxSubTableFormat4
 	mark_set (false),
 	mark (0) {}
 
-    inline bool is_actionable (StateTableDriver<MorxTypes, EntryData> *driver HB_UNUSED,
+    inline bool is_actionable (StateTableDriver<Types, EntryData> *driver HB_UNUSED,
 			       const Entry<EntryData> *entry)
     {
       return entry->data.ankrActionIndex != 0xFFFF;
     }
-    inline bool transition (StateTableDriver<MorxTypes, EntryData> *driver,
+    inline bool transition (StateTableDriver<Types, EntryData> *driver,
 			    const Entry<EntryData> *entry)
     {
       hb_buffer_t *buffer = driver->buffer;
@@ -619,7 +621,7 @@ struct KerxSubTableFormat4
 
     driver_context_t dc (this, c);
 
-    StateTableDriver<MorxTypes, EntryData> driver (machine, c->buffer, c->font->face);
+    StateTableDriver<Types, EntryData> driver (machine, c->buffer, c->font->face);
     driver.drive (&dc);
 
     return_trace (true);
@@ -634,10 +636,9 @@ struct KerxSubTableFormat4
   }
 
   protected:
-  KernSubTableHeader	header;
-  StateTable<MorxTypes, EntryData>
-			machine;
-  HBUINT32		flags;
+  KernSubTableHeader		header;
+  StateTable<Types, EntryData>	machine;
+  HBUINT32			flags;
   public:
   DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 20);
 };
@@ -756,7 +757,7 @@ struct KerxSubTableFormat6
 
 struct KerxSubTableHeader
 {
-  typedef MorxTypes Types;
+  typedef ExtendedTypes Types;
 
   unsigned int tuple_count (void) const { return tupleCount; }
 
diff --git a/src/hb-aat-layout-morx-table.hh b/src/hb-aat-layout-morx-table.hh
index 51f14682..a364f7ac 100644
--- a/src/hb-aat-layout-morx-table.hh
+++ b/src/hb-aat-layout-morx-table.hh
@@ -1166,11 +1166,11 @@ struct mortmorx
   DEFINE_SIZE_MIN (8);
 };
 
-struct morx : mortmorx<MorxTypes>
+struct morx : mortmorx<ExtendedTypes>
 {
   static const hb_tag_t tableTag	= HB_AAT_TAG_morx;
 };
-struct mort : mortmorx<MortTypes>
+struct mort : mortmorx<ObsoleteTypes>
 {
   static const hb_tag_t tableTag	= HB_AAT_TAG_mort;
 };
diff --git a/src/hb-ot-kern-table.hh b/src/hb-ot-kern-table.hh
index 6f1b46a9..3f771d2d 100644
--- a/src/hb-ot-kern-table.hh
+++ b/src/hb-ot-kern-table.hh
@@ -316,7 +316,7 @@ struct KernOT : KernTable<KernOT>
 
   struct SubTableHeader
   {
-    typedef AAT::MortTypes Types;
+    typedef AAT::ObsoleteTypes Types;
 
     unsigned int tuple_count (void) const { return 0; }
 
@@ -363,7 +363,7 @@ struct KernAAT : KernTable<KernAAT>
 
   struct SubTableHeader
   {
-    typedef AAT::MortTypes Types;
+    typedef AAT::ObsoleteTypes Types;
 
     unsigned int tuple_count (void) const { return 0; }
 
commit c808e444da12840ac3ab1d78569504b9b7e876f9
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 11:28:36 2018 -0500

    [kern/kerx] Share Format1 subtable

diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh
index c2b989c1..2b26d496 100644
--- a/src/hb-aat-layout-kerx-table.hh
+++ b/src/hb-aat-layout-kerx-table.hh
@@ -250,12 +250,12 @@ struct KerxSubTableFormat1
      * from the actual stateTableOffset, use it as the initial state."
      */
 
-    inline bool is_actionable (StateTableDriver<MorxTypes, EntryData> *driver HB_UNUSED,
+    inline bool is_actionable (StateTableDriver<Types, EntryData> *driver HB_UNUSED,
 			       const Entry<EntryData> *entry)
     {
       return Format1EntryT::performAction (entry);
     }
-    inline bool transition (StateTableDriver<MorxTypes, EntryData> *driver,
+    inline bool transition (StateTableDriver<Types, EntryData> *driver,
 			    const Entry<EntryData> *entry)
     {
       hb_buffer_t *buffer = driver->buffer;
@@ -376,15 +376,12 @@ struct KerxSubTableFormat1
     if (!c->plan->requested_kerning)
       return false;
 
-    if (header.coverage & header.CrossStream)
-      return false;
-
     if (header.tuple_count ())
       return_trace (false); /* TODO kerxTupleKern */
 
     driver_context_t dc (this, c);
 
-    StateTableDriver<MorxTypes, EntryData> driver (machine, c->buffer, c->font->face);
+    StateTableDriver<Types, EntryData> driver (machine, c->buffer, c->font->face);
     driver.drive (&dc);
 
     return_trace (true);
diff --git a/src/hb-ot-kern-table.hh b/src/hb-ot-kern-table.hh
index e205b4b3..6f1b46a9 100644
--- a/src/hb-ot-kern-table.hh
+++ b/src/hb-ot-kern-table.hh
@@ -42,191 +42,6 @@ namespace OT {
 
 
 template <typename KernSubTableHeader>
-struct KernSubTableFormat1
-{
-  typedef void EntryData;
-
-  struct driver_context_t
-  {
-    static const bool in_place = true;
-    enum Flags
-    {
-      Push		= 0x8000,	/* If set, push this glyph on the kerning stack. */
-      DontAdvance	= 0x4000,	/* If set, don't advance to the next glyph
-					 * before going to the new state. */
-      Offset		= 0x3FFF,	/* Byte offset from beginning of subtable to the
-					 * value table for the glyphs on the kerning stack. */
-    };
-
-    inline driver_context_t (const KernSubTableFormat1 *table_,
-			     AAT::hb_aat_apply_context_t *c_) :
-	c (c_),
-	table (table_),
-	/* Apparently the offset kernAction is from the beginning of the state-machine,
-	 * similar to offsets in morx table, NOT from beginning of this table, like
-	 * other subtables in kerx.  Discovered via testing. */
-	kernAction (&table->machine + table->kernAction),
-	depth (0),
-	crossStream (table->header.coverage & table->header.CrossStream),
-	crossOffset (0) {}
-
-    /* TODO
-     *
-     * "Because the stateTableOffset in the state table header is (strictly
-     * speaking) redundant, some 'kern' tables use it to record an initial
-     * state where that should not be StartOfText. To determine if this is
-     * done, calculate what the stateTableOffset should be. If it's different
-     * from the actual stateTableOffset, use it as the initial state."
-     */
-
-    inline bool is_actionable (AAT::StateTableDriver<AAT::MortTypes, EntryData> *driver HB_UNUSED,
-			       const AAT::Entry<EntryData> *entry)
-    {
-      return entry->flags & Offset;
-    }
-    inline bool transition (AAT::StateTableDriver<AAT::MortTypes, EntryData> *driver,
-			    const AAT::Entry<EntryData> *entry)
-    {
-      hb_buffer_t *buffer = driver->buffer;
-      unsigned int flags = entry->flags;
-
-      if (flags & Push)
-      {
-	if (likely (depth < ARRAY_LENGTH (stack)))
-	  stack[depth++] = buffer->idx;
-	else
-	  depth = 0; /* Probably not what CoreText does, but better? */
-      }
-
-      if (entry->flags & Offset)
-      {
-	unsigned int kernIndex = AAT::MortTypes::offsetToIndex (entry->flags & Offset,
-								&table->machine,
-								kernAction.arrayZ);
-	const FWORD *actions = &kernAction[kernIndex];
-	if (!c->sanitizer.check_array (actions, depth))
-	{
-	  depth = 0;
-	  return false;
-	}
-
-	hb_mask_t kern_mask = c->plan->kern_mask;
-
-	/* "Each pops one glyph from the kerning stack and applies the kerning value to it.
-	 * The end of the list is marked by an odd value... */
-	unsigned int i;
-	for (i = 0; i < depth; i++)
-	  if (actions[i] & 1)
-	  {
-	    i++;
-	    break;
-	  }
-	for (; i; i--)
-	{
-	  unsigned int idx = stack[depth - i];
-	  int v = actions[i - 1];
-
-	  /* "The end of the list is marked by an odd value..."
-	   * Ignore it. */
-	  v &= ~1;
-
-	  /* The following flag is undocumented in the spec, but described
-	   * in the example. */
-	  if (v == -0x8000)
-	  {
-	    crossOffset = 0;
-	    v = 0;
-	  }
-
-	  if (idx < buffer->len && buffer->info[idx].mask & kern_mask)
-	  {
-	    if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
-	    {
-	      if (crossStream)
-	      {
-		crossOffset += v;
-		if (!buffer->pos[idx].y_offset)
-		  buffer->pos[idx].y_offset += c->font->em_scale_y (crossOffset);
-	      }
-	      else
-	      {
-		if (!buffer->pos[idx].x_offset)
-		{
-		  buffer->pos[idx].x_advance += c->font->em_scale_x (v);
-		  buffer->pos[idx].x_offset += c->font->em_scale_x (v);
-		}
-	      }
-	    }
-	    else
-	    {
-	      if (crossStream)
-	      {
-	        /* CoreText doesn't do crossStream kerning in vertical.  We do. */
-		crossOffset += v;
-		if (!buffer->pos[idx].x_offset)
-		  buffer->pos[idx].x_offset = c->font->em_scale_x (crossOffset);
-	      }
-	      else
-	      {
-		if (!buffer->pos[idx].y_offset)
-		{
-		  buffer->pos[idx].y_advance += c->font->em_scale_y (v);
-		  buffer->pos[idx].y_offset += c->font->em_scale_y (v);
-		}
-	      }
-	    }
-	  }
-	}
-	depth = 0;
-      }
-      else
-	buffer->pos[buffer->idx].y_offset += c->font->em_scale_y (crossOffset);
-
-      return true;
-    }
-
-    private:
-    AAT::hb_aat_apply_context_t *c;
-    const KernSubTableFormat1 *table;
-    const UnsizedArrayOf<FWORD> &kernAction;
-    unsigned int stack[8];
-    unsigned int depth;
-    bool crossStream;
-    int crossOffset;
-  };
-
-  inline bool apply (AAT::hb_aat_apply_context_t *c) const
-  {
-    TRACE_APPLY (this);
-
-    if (!c->plan->requested_kerning)
-      return false;
-
-    driver_context_t dc (this, c);
-
-    AAT::StateTableDriver<AAT::MortTypes, EntryData> driver (machine, c->buffer, c->font->face);
-    driver.drive (&dc);
-
-    return_trace (true);
-  }
-
-  inline bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    /* The rest of array sanitizations are done at run-time. */
-    return_trace (likely (c->check_struct (this) &&
-			  machine.sanitize (c)));
-  }
-
-  protected:
-  KernSubTableHeader					header;
-  AAT::StateTable<AAT::MortTypes, EntryData>		machine;
-  OffsetTo<UnsizedArrayOf<FWORD>, HBUINT16, false>	kernAction;
-  public:
-  DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 10);
-};
-
-template <typename KernSubTableHeader>
 struct KernSubTableFormat2
 {
   inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right,
@@ -405,7 +220,7 @@ struct KernSubTable
   union {
   KernSubTableHeader				header;
   AAT::KerxSubTableFormat0<KernSubTableHeader>	format0;
-  KernSubTableFormat1<KernSubTableHeader>	format1;
+  AAT::KerxSubTableFormat1<KernSubTableHeader>	format1;
   KernSubTableFormat2<KernSubTableHeader>	format2;
   KernSubTableFormat3<KernSubTableHeader>	format3;
   } u;
commit a244190afa90ac253724a2ff23a3bdf0c507d0e6
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 11:43:25 2018 -0500

    [kerx] Minor

diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh
index 66b8b8e6..c2b989c1 100644
--- a/src/hb-aat-layout-kerx-table.hh
+++ b/src/hb-aat-layout-kerx-table.hh
@@ -524,7 +524,6 @@ struct KerxSubTableFormat4
 			    const Entry<EntryData> *entry)
     {
       hb_buffer_t *buffer = driver->buffer;
-      unsigned int flags = entry->flags;
 
       if (mark_set && entry->data.ankrActionIndex != 0xFFFF && buffer->idx < buffer->len)
       {
@@ -600,7 +599,7 @@ struct KerxSubTableFormat4
 	buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
       }
 
-      if (flags & Mark)
+      if (entry->flags & Mark)
       {
 	mark_set = true;
 	mark = buffer->idx;
commit 2a720911964a00ad607ff712be09ea3ea0925c9b
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 11:25:55 2018 -0500

    [kerx] Minor

diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh
index 34183270..66b8b8e6 100644
--- a/src/hb-aat-layout-kerx-table.hh
+++ b/src/hb-aat-layout-kerx-table.hh
@@ -311,6 +311,7 @@ struct KerxSubTableFormat1
 	    crossOffset = 0;
 	    v = 0;
 	  }
+
 	  if (idx < buffer->len && buffer->info[idx].mask & kern_mask)
 	  {
 	    if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
@@ -402,7 +403,7 @@ struct KerxSubTableFormat1
   StateTable<Types, EntryData>			machine;
   OffsetTo<UnsizedArrayOf<FWORD>, HBUINT, false>kernAction;
   public:
-  DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 20);
+  DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 5 * sizeof (HBUINT));
 };
 
 template <typename KernSubTableHeader>
commit f5f4ca7871ec2be2b5666a7b9e6e5e28133b8393
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 11:21:09 2018 -0500

    [kern/kerx] Enable crossStream kerning in vertical
    
    CoreText doesn't, but no reason we shouldn't do.

diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh
index 2a12a0b5..34183270 100644
--- a/src/hb-aat-layout-kerx-table.hh
+++ b/src/hb-aat-layout-kerx-table.hh
@@ -334,10 +334,10 @@ struct KerxSubTableFormat1
 	    {
 	      if (crossStream)
 	      {
-	        /* CoreText doesn't do crossStream kerning in vertical. */
-		//crossOffset += v;
-		//if (!buffer->pos[idx].x_offset)
-		//  buffer->pos[idx].x_offset = c->font->em_scale_x (crossOffset);
+	        /* CoreText doesn't do crossStream kerning in vertical.  We do. */
+		crossOffset += v;
+		if (!buffer->pos[idx].x_offset)
+		  buffer->pos[idx].x_offset = c->font->em_scale_x (crossOffset);
 	      }
 	      else
 	      {
diff --git a/src/hb-ot-kern-table.hh b/src/hb-ot-kern-table.hh
index e065b6ff..e205b4b3 100644
--- a/src/hb-ot-kern-table.hh
+++ b/src/hb-ot-kern-table.hh
@@ -161,10 +161,10 @@ struct KernSubTableFormat1
 	    {
 	      if (crossStream)
 	      {
-	        /* CoreText doesn't do crossStream kerning in vertical. */
-		//crossOffset += v;
-		//if (!buffer->pos[idx].x_offset)
-		//  buffer->pos[idx].x_offset = c->font->em_scale_x (crossOffset);
+	        /* CoreText doesn't do crossStream kerning in vertical.  We do. */
+		crossOffset += v;
+		if (!buffer->pos[idx].x_offset)
+		  buffer->pos[idx].x_offset = c->font->em_scale_x (crossOffset);
 	      }
 	      else
 	      {
commit d5c88af4a23bffc09840c43e6b1403b64a9f74d5
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 11:20:14 2018 -0500

    [kerx] More towards sharing Format1

diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh
index e7237823..2a12a0b5 100644
--- a/src/hb-aat-layout-kerx-table.hh
+++ b/src/hb-aat-layout-kerx-table.hh
@@ -180,6 +180,12 @@ struct Format1Entry<true>
     public:
     DEFINE_SIZE_STATIC (2);
   };
+
+  static inline bool performAction (const Entry<EntryData> *entry)
+  { return entry->data.kernActionIndex != 0xFFFF; }
+
+  static inline unsigned int kernActionIndex (const Entry<EntryData> *entry)
+  { return entry->data.kernActionIndex; }
 };
 template <>
 struct Format1Entry<false>
@@ -196,6 +202,12 @@ struct Format1Entry<false>
   };
 
   typedef void EntryData;
+
+  static inline bool performAction (const Entry<EntryData> *entry)
+  { return entry->flags & Offset; }
+
+  static inline unsigned int kernActionIndex (const Entry<EntryData> *entry)
+  { return entry->flags & Offset; }
 };
 
 template <typename KernSubTableHeader>
@@ -215,9 +227,10 @@ struct KerxSubTableFormat1
       DontAdvance	= Format1EntryT::DontAdvance,
     };
 
-    inline driver_context_t (const KerxSubTableFormat1 *table,
+    inline driver_context_t (const KerxSubTableFormat1 *table_,
 			     hb_aat_apply_context_t *c_) :
 	c (c_),
+	table (table_),
 	/* Apparently the offset kernAction is from the beginning of the state-machine,
 	 * similar to offsets in morx table, NOT from beginning of this table, like
 	 * other subtables in kerx.  Discovered via testing. */
@@ -226,10 +239,21 @@ struct KerxSubTableFormat1
 	crossStream (table->header.coverage & table->header.CrossStream),
 	crossOffset (0) {}
 
+
+    /* TODO
+     * 'kern' table has this pecularity, we don't currently implement.
+     *
+     * "Because the stateTableOffset in the state table header is (strictly
+     * speaking) redundant, some 'kern' tables use it to record an initial
+     * state where that should not be StartOfText. To determine if this is
+     * done, calculate what the stateTableOffset should be. If it's different
+     * from the actual stateTableOffset, use it as the initial state."
+     */
+
     inline bool is_actionable (StateTableDriver<MorxTypes, EntryData> *driver HB_UNUSED,
 			       const Entry<EntryData> *entry)
     {
-      return entry->data.kernActionIndex != 0xFFFF;
+      return Format1EntryT::performAction (entry);
     }
     inline bool transition (StateTableDriver<MorxTypes, EntryData> *driver,
 			    const Entry<EntryData> *entry)
@@ -248,9 +272,11 @@ struct KerxSubTableFormat1
 	  depth = 0; /* Probably not what CoreText does, but better? */
       }
 
-      if (entry->data.kernActionIndex != 0xFFFF)
+      if (Format1EntryT::performAction (entry))
       {
-	const FWORD *actions = &kernAction[entry->data.kernActionIndex];
+	unsigned int kern_idx = Format1EntryT::kernActionIndex (entry);
+	kern_idx = Types::offsetToIndex (kern_idx, &table->machine, kernAction.arrayZ);
+	const FWORD *actions = &kernAction[kern_idx];
 	if (!c->sanitizer.check_array (actions, depth))
 	{
 	  depth = 0;
@@ -334,6 +360,7 @@ struct KerxSubTableFormat1
 
     private:
     hb_aat_apply_context_t *c;
+    const KerxSubTableFormat1 *table;
     const UnsizedArrayOf<FWORD> &kernAction;
     unsigned int stack[8];
     unsigned int depth;
diff --git a/src/hb-aat-layout-morx-table.hh b/src/hb-aat-layout-morx-table.hh
index 784b36cf..51f14682 100644
--- a/src/hb-aat-layout-morx-table.hh
+++ b/src/hb-aat-layout-morx-table.hh
@@ -410,7 +410,7 @@ struct LigatureEntry<false>
   { return entry->flags & Offset; }
 
   static inline unsigned int ligActionIndex (const Entry<EntryData> *entry)
-  { return entry->flags & 0x3FFF; }
+  { return entry->flags & Offset; }
 };
 
 
@@ -479,9 +479,6 @@ struct LigatureSubtable
       {
 	DEBUG_MSG (APPLY, nullptr, "Perform action with %d", match_length);
 	unsigned int end = buffer->out_len;
-	unsigned int action_idx = LigatureEntryT::ligActionIndex (entry);
-	unsigned int action;
-	unsigned int ligature_idx = 0;
 
 	if (unlikely (!match_length))
 	  return true;
@@ -490,8 +487,13 @@ struct LigatureSubtable
 	  return false; // TODO Work on previous instead?
 
 	unsigned int cursor = match_length;
+
+	unsigned int action_idx = LigatureEntryT::ligActionIndex (entry);
 	action_idx = Types::offsetToIndex (action_idx, table, ligAction.arrayZ);
 	const HBUINT32 *actionData = &ligAction[action_idx];
+
+	unsigned int ligature_idx = 0;
+	unsigned int action;
         do
 	{
 	  if (unlikely (!cursor))
commit b693fd0dc6c7979dcacdff060ecf12a2e107071d
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 11:05:28 2018 -0500

    [morx] Simplify

diff --git a/src/hb-aat-layout-morx-table.hh b/src/hb-aat-layout-morx-table.hh
index c06af421..784b36cf 100644
--- a/src/hb-aat-layout-morx-table.hh
+++ b/src/hb-aat-layout-morx-table.hh
@@ -384,12 +384,10 @@ struct LigatureEntry<true>
     DEFINE_SIZE_STATIC (2);
   };
 
-  template <typename Flags>
-  static inline bool performAction (Flags flags)
-  { return flags & PerformAction; }
+  static inline bool performAction (const Entry<EntryData> *entry)
+  { return entry->flags & PerformAction; }
 
-  template <typename Entry, typename Flags>
-  static inline unsigned int ligActionIndex (Entry &entry, Flags flags)
+  static inline unsigned int ligActionIndex (const Entry<EntryData> *entry)
   { return entry->data.ligActionIndex; }
 };
 template <>
@@ -408,13 +406,11 @@ struct LigatureEntry<false>
 
   typedef void EntryData;
 
-  template <typename Flags>
-  static inline bool performAction (Flags flags)
-  { return flags & Offset; }
+  static inline bool performAction (const Entry<EntryData> *entry)
+  { return entry->flags & Offset; }
 
-  template <typename Entry, typename Flags>
-  static inline unsigned int ligActionIndex (Entry &entry, Flags flags)
-  { return flags & 0x3FFF; }
+  static inline unsigned int ligActionIndex (const Entry<EntryData> *entry)
+  { return entry->flags & 0x3FFF; }
 };
 
 
@@ -458,16 +454,15 @@ struct LigatureSubtable
     inline bool is_actionable (StateTableDriver<Types, EntryData> *driver HB_UNUSED,
 			       const Entry<EntryData> *entry)
     {
-      return LigatureEntryT::performAction (entry->flags);
+      return LigatureEntryT::performAction (entry);
     }
     inline bool transition (StateTableDriver<Types, EntryData> *driver,
 			    const Entry<EntryData> *entry)
     {
       hb_buffer_t *buffer = driver->buffer;
-      unsigned int flags = entry->flags;
 
       DEBUG_MSG (APPLY, nullptr, "Ligature transition at %d", buffer->idx);
-      if (flags & LigatureEntryT::SetComponent)
+      if (entry->flags & LigatureEntryT::SetComponent)
       {
         if (unlikely (match_length >= ARRAY_LENGTH (match_positions)))
 	  return false;
@@ -480,11 +475,11 @@ struct LigatureSubtable
 	DEBUG_MSG (APPLY, nullptr, "Set component at %d", buffer->out_len);
       }
 
-      if (LigatureEntryT::performAction (flags))
+      if (LigatureEntryT::performAction (entry))
       {
 	DEBUG_MSG (APPLY, nullptr, "Perform action with %d", match_length);
 	unsigned int end = buffer->out_len;
-	unsigned int action_idx = LigatureEntryT::ligActionIndex (entry, flags);
+	unsigned int action_idx = LigatureEntryT::ligActionIndex (entry);
 	unsigned int action;
 	unsigned int ligature_idx = 0;
 
commit ce3451dc2aad2241c148953842e696e9f53b5deb
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 11:02:04 2018 -0500

    [kerx] Towards sharing Format1

diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh
index 9cdba879..e7237823 100644
--- a/src/hb-aat-layout-kerx-table.hh
+++ b/src/hb-aat-layout-kerx-table.hh
@@ -156,9 +156,22 @@ struct KerxSubTableFormat0
   DEFINE_SIZE_ARRAY (KernSubTableHeader::static_size + 16, pairs);
 };
 
-template <typename KernSubTableHeader>
-struct KerxSubTableFormat1
+
+template <bool extended>
+struct Format1Entry;
+
+template <>
+struct Format1Entry<true>
 {
+  enum Flags
+  {
+    Push		= 0x8000,	/* If set, push this glyph on the kerning stack. */
+    DontAdvance		= 0x4000,	/* If set, don't advance to the next glyph
+					 * before going to the new state. */
+    Reset		= 0x2000,	/* If set, reset the kerning data (clear the stack) */
+    Reserved		= 0x1FFF,	/* Not used; set to 0. */
+  };
+
   struct EntryData
   {
     HBUINT16	kernActionIndex;/* Index into the kerning value array. If
@@ -167,17 +180,39 @@ struct KerxSubTableFormat1
     public:
     DEFINE_SIZE_STATIC (2);
   };
+};
+template <>
+struct Format1Entry<false>
+{
+  enum Flags
+  {
+    Push		= 0x8000,	/* If set, push this glyph on the kerning stack. */
+    DontAdvance		= 0x4000,	/* If set, don't advance to the next glyph
+					 * before going to the new state. */
+    Offset		= 0x3FFF,	/* Byte offset from beginning of subtable to the
+					 * value table for the glyphs on the kerning stack. */
+
+    Reset		= 0x0000,	/* Not supported? */
+  };
+
+  typedef void EntryData;
+};
+
+template <typename KernSubTableHeader>
+struct KerxSubTableFormat1
+{
+  typedef typename KernSubTableHeader::Types Types;
+  typedef typename Types::HBUINT HBUINT;
+
+  typedef Format1Entry<Types::extended> Format1EntryT;
+  typedef typename Format1EntryT::EntryData EntryData;
 
   struct driver_context_t
   {
     static const bool in_place = true;
-    enum Flags
+    enum
     {
-      Push		= 0x8000,	/* If set, push this glyph on the kerning stack. */
-      DontAdvance	= 0x4000,	/* If set, don't advance to the next glyph
-					 * before going to the new state. */
-      Reset		= 0x2000,	/* If set, reset the kerning data (clear the stack) */
-      Reserved		= 0x1FFF,	/* Not used; set to 0. */
+      DontAdvance	= Format1EntryT::DontAdvance,
     };
 
     inline driver_context_t (const KerxSubTableFormat1 *table,
@@ -202,12 +237,10 @@ struct KerxSubTableFormat1
       hb_buffer_t *buffer = driver->buffer;
       unsigned int flags = entry->flags;
 
-      if (flags & Reset)
-      {
+      if (flags & Format1EntryT::Reset)
 	depth = 0;
-      }
 
-      if (flags & Push)
+      if (flags & Format1EntryT::Push)
       {
 	if (likely (depth < ARRAY_LENGTH (stack)))
 	  stack[depth++] = buffer->idx;
@@ -339,8 +372,8 @@ struct KerxSubTableFormat1
 
   protected:
   KernSubTableHeader				header;
-  StateTable<MorxTypes, EntryData>		machine;
-  LOffsetTo<UnsizedArrayOf<FWORD>, false>	kernAction;
+  StateTable<Types, EntryData>			machine;
+  OffsetTo<UnsizedArrayOf<FWORD>, HBUINT, false>kernAction;
   public:
   DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 20);
 };
commit e890753ebbf0d20c1c86796837918d530610df3b
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 10:58:50 2018 -0500

    [morx] Minor

diff --git a/src/hb-aat-layout-morx-table.hh b/src/hb-aat-layout-morx-table.hh
index 2bc60182..c06af421 100644
--- a/src/hb-aat-layout-morx-table.hh
+++ b/src/hb-aat-layout-morx-table.hh
@@ -375,14 +375,14 @@ struct LigatureEntry<true>
     Reserved		= 0x1FFF,	/* These bits are reserved and should be set to 0. */
   };
 
-  typedef struct
+  struct EntryData
   {
     HBUINT16	ligActionIndex;	/* Index to the first ligActionTable entry
 				 * for processing this group, if indicated
 				 * by the flags. */
     public:
     DEFINE_SIZE_STATIC (2);
-  } EntryData;
+  };
 
   template <typename Flags>
   static inline bool performAction (Flags flags)
@@ -428,11 +428,11 @@ struct LigatureSubtable
 
   struct driver_context_t
   {
+    static const bool in_place = false;
     enum
     {
       DontAdvance	= LigatureEntryT::DontAdvance,
     };
-    static const bool in_place = false;
     enum LigActionFlags
     {
       LigActionLast	= 0x80000000,	/* This is the last action in the list. This also
commit 5b17853547ca6848ee652ef6990a81bb345ac06f
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 10:45:25 2018 -0500

    [kern/kerx] Share Format0

diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh
index dcab790d..9cdba879 100644
--- a/src/hb-aat-layout-kerx-table.hh
+++ b/src/hb-aat-layout-kerx-table.hh
@@ -93,6 +93,14 @@ struct KernPair
 template <typename KernSubTableHeader>
 struct KerxSubTableFormat0
 {
+  inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
+  {
+    hb_glyph_pair_t pair = {left, right};
+    int i = pairs.bsearch (pair);
+    if (i == -1) return 0;
+    return pairs[i].get_kerning ();
+  }
+
   inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right,
 			  hb_aat_apply_context_t *c) const
   {
@@ -100,7 +108,7 @@ struct KerxSubTableFormat0
     int i = pairs.bsearch (pair);
     if (i == -1) return 0;
     int v = pairs[i].get_kerning ();
-    return kerxTupleKern (v, header.tupleCount, this, c);
+    return kerxTupleKern (v, header.tuple_count (), this, c);
   }
 
   inline bool apply (hb_aat_apply_context_t *c) const
@@ -310,7 +318,7 @@ struct KerxSubTableFormat1
     if (header.coverage & header.CrossStream)
       return false;
 
-    if (header.tupleCount)
+    if (header.tuple_count ())
       return_trace (false); /* TODO kerxTupleKern */
 
     driver_context_t dc (this, c);
@@ -349,7 +357,7 @@ struct KerxSubTableFormat2
     unsigned int offset = l + r;
     const FWORD *v = &StructAtOffset<FWORD> (&(this+array), offset);
     if (unlikely (!v->sanitize (&c->sanitizer))) return 0;
-    return kerxTupleKern (*v, header.tupleCount, this, c);
+    return kerxTupleKern (*v, header.tuple_count (), this, c);
   }
 
   inline bool apply (hb_aat_apply_context_t *c) const
@@ -601,7 +609,7 @@ struct KerxSubTableFormat6
       if (unlikely (hb_unsigned_mul_overflows (offset, sizeof (FWORD32)))) return 0;
       const FWORD32 *v = &StructAtOffset<FWORD32> (&(this+t.array), offset * sizeof (FWORD32));
       if (unlikely (!v->sanitize (&c->sanitizer))) return 0;
-      return kerxTupleKern (*v, header.tupleCount, &(this+vector), c);
+      return kerxTupleKern (*v, header.tuple_count (), &(this+vector), c);
     }
     else
     {
@@ -611,7 +619,7 @@ struct KerxSubTableFormat6
       unsigned int offset = l + r;
       const FWORD *v = &StructAtOffset<FWORD> (&(this+t.array), offset * sizeof (FWORD));
       if (unlikely (!v->sanitize (&c->sanitizer))) return 0;
-      return kerxTupleKern (*v, header.tupleCount, &(this+vector), c);
+      return kerxTupleKern (*v, header.tuple_count (), &(this+vector), c);
     }
   }
 
@@ -646,7 +654,7 @@ struct KerxSubTableFormat6
 			     u.s.columnIndexTable.sanitize (c, this) &&
 			     c->check_range (this, u.s.array)
 			   )) &&
-			  (header.tupleCount == 0 ||
+			  (header.tuple_count () == 0 ||
 			   c->check_range (this, vector))));
   }
 
@@ -693,6 +701,8 @@ struct KerxSubTableHeader
 {
   typedef MorxTypes Types;
 
+  unsigned int tuple_count (void) const { return tupleCount; }
+
   enum Coverage
   {
     Vertical		= 0x80000000,	/* Set if table has vertical kerning values. */
diff --git a/src/hb-ot-kern-table.hh b/src/hb-ot-kern-table.hh
index c7c15e88..e065b6ff 100644
--- a/src/hb-ot-kern-table.hh
+++ b/src/hb-ot-kern-table.hh
@@ -42,47 +42,6 @@ namespace OT {
 
 
 template <typename KernSubTableHeader>
-struct KernSubTableFormat0
-{
-  inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
-  {
-    AAT::hb_glyph_pair_t pair = {left, right};
-    int i = pairs.bsearch (pair);
-    if (i == -1) return 0;
-    return pairs[i].get_kerning ();
-  }
-
-  inline bool apply (AAT::hb_aat_apply_context_t *c) const
-  {
-    TRACE_APPLY (this);
-
-    if (!c->plan->requested_kerning)
-      return false;
-
-    if (header.coverage & header.CrossStream)
-      return false;
-
-    hb_kern_machine_t<KernSubTableFormat0> machine (*this);
-    machine.kern (c->font, c->buffer, c->plan->kern_mask);
-
-    return_trace (true);
-  }
-
-  inline bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (pairs.sanitize (c));
-  }
-
-  protected:
-  KernSubTableHeader		header;
-  BinSearchArrayOf<AAT::KernPair, typename KernSubTableHeader::Types::HBUINT>
-				pairs;	/* Array of kerning pairs. */
-  public:
-  DEFINE_SIZE_ARRAY (KernSubTableHeader::static_size + 8, pairs);
-};
-
-template <typename KernSubTableHeader>
 struct KernSubTableFormat1
 {
   typedef void EntryData;
@@ -445,7 +404,7 @@ struct KernSubTable
   public:
   union {
   KernSubTableHeader				header;
-  KernSubTableFormat0<KernSubTableHeader>	format0;
+  AAT::KerxSubTableFormat0<KernSubTableHeader>	format0;
   KernSubTableFormat1<KernSubTableHeader>	format1;
   KernSubTableFormat2<KernSubTableHeader>	format2;
   KernSubTableFormat3<KernSubTableHeader>	format3;
@@ -544,6 +503,8 @@ struct KernOT : KernTable<KernOT>
   {
     typedef AAT::MortTypes Types;
 
+    unsigned int tuple_count (void) const { return 0; }
+
     enum Coverage
     {
       Direction		= 0x01u,
@@ -589,6 +550,8 @@ struct KernAAT : KernTable<KernAAT>
   {
     typedef AAT::MortTypes Types;
 
+    unsigned int tuple_count (void) const { return 0; }
+
     enum Coverage
     {
       Direction		= 0x80u,
@@ -609,7 +572,8 @@ struct KernAAT : KernTable<KernAAT>
     HBUINT8	coverage;	/* Coverage bits. */
     HBUINT8	format;		/* Subtable format. */
     HBUINT16	tupleIndex;	/* The tuple index (used for variations fonts).
-				 * This value specifies which tuple this subtable covers. */
+				 * This value specifies which tuple this subtable covers.
+				 * Note: We don't implement. */
     public:
     DEFINE_SIZE_STATIC (8);
   };
commit c97dde5d55929df394fbe57c1ba1a725592c6732
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 10:39:39 2018 -0500

    [kern/kerx] Towards merge more

diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh
index a2f0c642..dcab790d 100644
--- a/src/hb-aat-layout-kerx-table.hh
+++ b/src/hb-aat-layout-kerx-table.hh
@@ -29,6 +29,7 @@
 #define HB_AAT_LAYOUT_KERX_TABLE_HH
 
 #include "hb-kern.hh"
+#include "hb-aat-layout-ankr-table.hh"
 
 /*
  * kerx -- Extended Kerning
@@ -57,36 +58,36 @@ kerxTupleKern (int value,
 }
 
 
-struct KerxSubTableHeader
+struct hb_glyph_pair_t
 {
-  typedef MorxTypes Types;
+  hb_codepoint_t left;
+  hb_codepoint_t right;
+};
 
-  enum Coverage
+struct KernPair
+{
+  inline int get_kerning (void) const
+  { return value; }
+
+  inline int cmp (const hb_glyph_pair_t &o) const
   {
-    Vertical		= 0x80000000,	/* Set if table has vertical kerning values. */
-    CrossStream		= 0x40000000,	/* Set if table has cross-stream kerning values. */
-    Variation		= 0x20000000,	/* Set if table has variation kerning values. */
-    Backwards		= 0x10000000,	/* If clear, process the glyphs forwards, that
-					 * is, from first to last in the glyph stream.
-					 * If we, process them from last to first.
-					 * This flag only applies to state-table based
-					 * 'kerx' subtables (types 1 and 4). */
-    Reserved		= 0x0FFFFF00,	/* Reserved, set to zero. */
-    SubtableType	= 0x000000FF,	/* Subtable type. */
-  };
+    int ret = left.cmp (o.left);
+    if (ret) return ret;
+    return right.cmp (o.right);
+  }
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (likely (c->check_struct (this)));
+    return_trace (c->check_struct (this));
   }
 
+  protected:
+  GlyphID	left;
+  GlyphID	right;
+  FWORD		value;
   public:
-  HBUINT32	length;
-  HBUINT32	coverage;
-  HBUINT32	tupleCount;
-  public:
-  DEFINE_SIZE_STATIC (12);
+  DEFINE_SIZE_STATIC (6);
 };
 
 template <typename KernSubTableHeader>
@@ -687,6 +688,39 @@ struct KerxSubTableFormat6
   DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 24);
 };
 
+
+struct KerxSubTableHeader
+{
+  typedef MorxTypes Types;
+
+  enum Coverage
+  {
+    Vertical		= 0x80000000,	/* Set if table has vertical kerning values. */
+    CrossStream		= 0x40000000,	/* Set if table has cross-stream kerning values. */
+    Variation		= 0x20000000,	/* Set if table has variation kerning values. */
+    Backwards		= 0x10000000,	/* If clear, process the glyphs forwards, that
+					 * is, from first to last in the glyph stream.
+					 * If we, process them from last to first.
+					 * This flag only applies to state-table based
+					 * 'kerx' subtables (types 1 and 4). */
+    Reserved		= 0x0FFFFF00,	/* Reserved, set to zero. */
+    SubtableType	= 0x000000FF,	/* Subtable type. */
+  };
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (likely (c->check_struct (this)));
+  }
+
+  public:
+  HBUINT32	length;
+  HBUINT32	coverage;
+  HBUINT32	tupleCount;
+  public:
+  DEFINE_SIZE_STATIC (12);
+};
+
 struct KerxTable
 {
   friend struct kerx;
@@ -816,6 +850,7 @@ struct kerx
   DEFINE_SIZE_MIN (8);
 };
 
+
 } /* namespace AAT */
 
 
diff --git a/src/hb-kern.hh b/src/hb-kern.hh
index de726960..60e625c4 100644
--- a/src/hb-kern.hh
+++ b/src/hb-kern.hh
@@ -114,39 +114,6 @@ struct hb_kern_machine_t
 };
 
 
-struct hb_glyph_pair_t
-{
-  hb_codepoint_t left;
-  hb_codepoint_t right;
-};
-
-struct KernPair
-{
-  inline int get_kerning (void) const
-  { return value; }
-
-  inline int cmp (const hb_glyph_pair_t &o) const
-  {
-    int ret = left.cmp (o.left);
-    if (ret) return ret;
-    return right.cmp (o.right);
-  }
-
-  inline bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this));
-  }
-
-  protected:
-  GlyphID	left;
-  GlyphID	right;
-  FWORD		value;
-  public:
-  DEFINE_SIZE_STATIC (6);
-};
-
-
 } /* namespace OT */
 
 
diff --git a/src/hb-ot-kern-table.hh b/src/hb-ot-kern-table.hh
index 9f754662..c7c15e88 100644
--- a/src/hb-ot-kern-table.hh
+++ b/src/hb-ot-kern-table.hh
@@ -27,8 +27,7 @@
 #ifndef HB_OT_KERN_TABLE_HH
 #define HB_OT_KERN_TABLE_HH
 
-#include "hb-kern.hh"
-#include "hb-ot-shape.hh"
+#include "hb-aat-layout-kerx-table.hh"
 
 
 /*
@@ -47,7 +46,7 @@ struct KernSubTableFormat0
 {
   inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
   {
-    hb_glyph_pair_t pair = {left, right};
+    AAT::hb_glyph_pair_t pair = {left, right};
     int i = pairs.bsearch (pair);
     if (i == -1) return 0;
     return pairs[i].get_kerning ();
@@ -77,7 +76,7 @@ struct KernSubTableFormat0
 
   protected:
   KernSubTableHeader		header;
-  BinSearchArrayOf<KernPair, typename KernSubTableHeader::Types::HBUINT>
+  BinSearchArrayOf<AAT::KernPair, typename KernSubTableHeader::Types::HBUINT>
 				pairs;	/* Array of kerning pairs. */
   public:
   DEFINE_SIZE_ARRAY (KernSubTableHeader::static_size + 8, pairs);
commit 540ccc38b0f95804d08047f8b2d059bfd1e09337
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 10:33:46 2018 -0500

    [kern/kerx] More towards sharing

diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh
index 11d7f5c7..a2f0c642 100644
--- a/src/hb-aat-layout-kerx-table.hh
+++ b/src/hb-aat-layout-kerx-table.hh
@@ -59,6 +59,8 @@ kerxTupleKern (int value,
 
 struct KerxSubTableHeader
 {
+  typedef MorxTypes Types;
+
   enum Coverage
   {
     Vertical		= 0x80000000,	/* Set if table has vertical kerning values. */
@@ -134,13 +136,12 @@ struct KerxSubTableFormat0
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (likely (c->check_struct (this) &&
-			  pairs.sanitize (c)));
+    return_trace (likely (pairs.sanitize (c)));
   }
 
   protected:
   KernSubTableHeader	header;
-  BinSearchArrayOf<KernPair, HBUINT32>
+  BinSearchArrayOf<KernPair, typename KernSubTableHeader::Types::HBUINT>
 			pairs;	/* Sorted kern records. */
   public:
   DEFINE_SIZE_ARRAY (KernSubTableHeader::static_size + 16, pairs);
diff --git a/src/hb-ot-kern-table.hh b/src/hb-ot-kern-table.hh
index 40d36a33..9f754662 100644
--- a/src/hb-ot-kern-table.hh
+++ b/src/hb-ot-kern-table.hh
@@ -49,8 +49,7 @@ struct KernSubTableFormat0
   {
     hb_glyph_pair_t pair = {left, right};
     int i = pairs.bsearch (pair);
-    if (i == -1)
-      return 0;
+    if (i == -1) return 0;
     return pairs[i].get_kerning ();
   }
 
@@ -78,7 +77,8 @@ struct KernSubTableFormat0
 
   protected:
   KernSubTableHeader		header;
-  BinSearchArrayOf<KernPair>	pairs;	/* Array of kerning pairs. */
+  BinSearchArrayOf<KernPair, typename KernSubTableHeader::Types::HBUINT>
+				pairs;	/* Array of kerning pairs. */
   public:
   DEFINE_SIZE_ARRAY (KernSubTableHeader::static_size + 8, pairs);
 };
@@ -543,6 +543,8 @@ struct KernOT : KernTable<KernOT>
 
   struct SubTableHeader
   {
+    typedef AAT::MortTypes Types;
+
     enum Coverage
     {
       Direction		= 0x01u,
@@ -586,6 +588,8 @@ struct KernAAT : KernTable<KernAAT>
 
   struct SubTableHeader
   {
+    typedef AAT::MortTypes Types;
+
     enum Coverage
     {
       Direction		= 0x80u,
commit d0f8f4c200670bc0bfbffbf301139a3613865a7f
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 10:25:25 2018 -0500

    [kern] Move kern machine to hb-kern.hh

diff --git a/src/Makefile.sources b/src/Makefile.sources
index 561fbed6..2cada4bb 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -16,6 +16,7 @@ HB_BASE_sources = \
 	hb-font.hh \
 	hb-font.cc \
 	hb-iter.hh \
+	hb-kern.hh \
 	hb-map.hh \
 	hb-map.cc \
 	hb-machinery.hh \
diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh
index 995e0138..11d7f5c7 100644
--- a/src/hb-aat-layout-kerx-table.hh
+++ b/src/hb-aat-layout-kerx-table.hh
@@ -28,10 +28,7 @@
 #ifndef HB_AAT_LAYOUT_KERX_TABLE_HH
 #define HB_AAT_LAYOUT_KERX_TABLE_HH
 
-#include "hb-open-type.hh"
-#include "hb-aat-layout-common.hh"
-#include "hb-ot-layout-gpos-table.hh"
-#include "hb-ot-kern-table.hh"
+#include "hb-kern.hh"
 
 /*
  * kerx -- Extended Kerning
diff --git a/src/hb-kern.hh b/src/hb-kern.hh
new file mode 100644
index 00000000..de726960
--- /dev/null
+++ b/src/hb-kern.hh
@@ -0,0 +1,153 @@
+/*
+ * Copyright © 2017  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_KERN_HH
+#define HB_KERN_HH
+
+#include "hb-open-type.hh"
+#include "hb-aat-layout-common.hh"
+#include "hb-ot-layout-gpos-table.hh"
+
+
+namespace OT {
+
+
+template <typename Driver>
+struct hb_kern_machine_t
+{
+  hb_kern_machine_t (const Driver &driver_) : driver (driver_) {}
+
+  HB_NO_SANITIZE_SIGNED_INTEGER_OVERFLOW
+  inline void kern (hb_font_t   *font,
+		    hb_buffer_t *buffer,
+		    hb_mask_t    kern_mask,
+		    bool         scale = true) const
+  {
+    OT::hb_ot_apply_context_t c (1, font, buffer);
+    c.set_lookup_mask (kern_mask);
+    c.set_lookup_props (OT::LookupFlag::IgnoreMarks);
+    OT::hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c.iter_input;
+    skippy_iter.init (&c);
+
+    bool horizontal = HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction);
+    unsigned int count = buffer->len;
+    hb_glyph_info_t *info = buffer->info;
+    hb_glyph_position_t *pos = buffer->pos;
+    for (unsigned int idx = 0; idx < count;)
+    {
+      if (!(info[idx].mask & kern_mask))
+      {
+	idx++;
+	continue;
+      }
+
+      skippy_iter.reset (idx, 1);
+      if (!skippy_iter.next ())
+      {
+	idx++;
+	continue;
+      }
+
+      unsigned int i = idx;
+      unsigned int j = skippy_iter.idx;
+
+      hb_position_t kern = driver.get_kerning (info[i].codepoint,
+					       info[j].codepoint);
+
+
+      if (likely (!kern))
+        goto skip;
+
+
+      if (horizontal)
+      {
+        if (scale)
+	  kern = font->em_scale_x (kern);
+	hb_position_t kern1 = kern >> 1;
+	hb_position_t kern2 = kern - kern1;
+	pos[i].x_advance += kern1;
+	pos[j].x_advance += kern2;
+	pos[j].x_offset += kern2;
+      }
+      else
+      {
+        if (scale)
+	  kern = font->em_scale_y (kern);
+	hb_position_t kern1 = kern >> 1;
+	hb_position_t kern2 = kern - kern1;
+	pos[i].y_advance += kern1;
+	pos[j].y_advance += kern2;
+	pos[j].y_offset += kern2;
+      }
+
+      buffer->unsafe_to_break (i, j + 1);
+
+    skip:
+      idx = skippy_iter.idx;
+    }
+  }
+
+  const Driver &driver;
+};
+
+
+struct hb_glyph_pair_t
+{
+  hb_codepoint_t left;
+  hb_codepoint_t right;
+};
+
+struct KernPair
+{
+  inline int get_kerning (void) const
+  { return value; }
+
+  inline int cmp (const hb_glyph_pair_t &o) const
+  {
+    int ret = left.cmp (o.left);
+    if (ret) return ret;
+    return right.cmp (o.right);
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  protected:
+  GlyphID	left;
+  GlyphID	right;
+  FWORD		value;
+  public:
+  DEFINE_SIZE_STATIC (6);
+};
+
+
+} /* namespace OT */
+
+
+#endif /* HB_KERN_HH */
diff --git a/src/hb-ot-kern-table.hh b/src/hb-ot-kern-table.hh
index 33e68271..40d36a33 100644
--- a/src/hb-ot-kern-table.hh
+++ b/src/hb-ot-kern-table.hh
@@ -27,89 +27,8 @@
 #ifndef HB_OT_KERN_TABLE_HH
 #define HB_OT_KERN_TABLE_HH
 
-#include "hb-open-type.hh"
+#include "hb-kern.hh"
 #include "hb-ot-shape.hh"
-#include "hb-ot-layout-gsubgpos.hh"
-#include "hb-aat-layout-common.hh"
-
-
-template <typename Driver>
-struct hb_kern_machine_t
-{
-  hb_kern_machine_t (const Driver &driver_) : driver (driver_) {}
-
-  HB_NO_SANITIZE_SIGNED_INTEGER_OVERFLOW
-  inline void kern (hb_font_t   *font,
-		    hb_buffer_t *buffer,
-		    hb_mask_t    kern_mask,
-		    bool         scale = true) const
-  {
-    OT::hb_ot_apply_context_t c (1, font, buffer);
-    c.set_lookup_mask (kern_mask);
-    c.set_lookup_props (OT::LookupFlag::IgnoreMarks);
-    OT::hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c.iter_input;
-    skippy_iter.init (&c);
-
-    bool horizontal = HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction);
-    unsigned int count = buffer->len;
-    hb_glyph_info_t *info = buffer->info;
-    hb_glyph_position_t *pos = buffer->pos;
-    for (unsigned int idx = 0; idx < count;)
-    {
-      if (!(info[idx].mask & kern_mask))
-      {
-	idx++;
-	continue;
-      }
-
-      skippy_iter.reset (idx, 1);
-      if (!skippy_iter.next ())
-      {
-	idx++;
-	continue;
-      }
-
-      unsigned int i = idx;
-      unsigned int j = skippy_iter.idx;
-
-      hb_position_t kern = driver.get_kerning (info[i].codepoint,
-					       info[j].codepoint);
-
-
-      if (likely (!kern))
-        goto skip;
-
-
-      if (horizontal)
-      {
-        if (scale)
-	  kern = font->em_scale_x (kern);
-	hb_position_t kern1 = kern >> 1;
-	hb_position_t kern2 = kern - kern1;
-	pos[i].x_advance += kern1;
-	pos[j].x_advance += kern2;
-	pos[j].x_offset += kern2;
-      }
-      else
-      {
-        if (scale)
-	  kern = font->em_scale_y (kern);
-	hb_position_t kern1 = kern >> 1;
-	hb_position_t kern2 = kern - kern1;
-	pos[i].y_advance += kern1;
-	pos[j].y_advance += kern2;
-	pos[j].y_offset += kern2;
-      }
-
-      buffer->unsafe_to_break (i, j + 1);
-
-    skip:
-      idx = skippy_iter.idx;
-    }
-  }
-
-  const Driver &driver;
-};
 
 
 /*
@@ -123,38 +42,6 @@ struct hb_kern_machine_t
 namespace OT {
 
 
-struct hb_glyph_pair_t
-{
-  hb_codepoint_t left;
-  hb_codepoint_t right;
-};
-
-struct KernPair
-{
-  inline int get_kerning (void) const
-  { return value; }
-
-  inline int cmp (const hb_glyph_pair_t &o) const
-  {
-    int ret = left.cmp (o.left);
-    if (ret) return ret;
-    return right.cmp (o.right);
-  }
-
-  inline bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this));
-  }
-
-  protected:
-  GlyphID	left;
-  GlyphID	right;
-  FWORD		value;
-  public:
-  DEFINE_SIZE_STATIC (6);
-};
-
 template <typename KernSubTableHeader>
 struct KernSubTableFormat0
 {
diff --git a/src/hb-ot-shape-fallback.cc b/src/hb-ot-shape-fallback.cc
index 766efe20..3742d670 100644
--- a/src/hb-ot-shape-fallback.cc
+++ b/src/hb-ot-shape-fallback.cc
@@ -465,7 +465,7 @@ _hb_ot_shape_fallback_kern (const hb_ot_shape_plan_t *plan,
       !font->has_glyph_v_kerning_func ())
     return;
   hb_ot_shape_fallback_kern_driver_t driver (font, buffer);
-  hb_kern_machine_t<hb_ot_shape_fallback_kern_driver_t> machine (driver);
+  OT::hb_kern_machine_t<hb_ot_shape_fallback_kern_driver_t> machine (driver);
   machine.kern (font, buffer, plan->kern_mask, false);
 }
 
commit a6acff252c72457ecfa856fd6c57081b3a4290dd
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 10:19:46 2018 -0500

    [kerx] Towards sharing subtables with kern

diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh
index 6f63aa40..995e0138 100644
--- a/src/hb-aat-layout-kerx-table.hh
+++ b/src/hb-aat-layout-kerx-table.hh
@@ -90,6 +90,7 @@ struct KerxSubTableHeader
   DEFINE_SIZE_STATIC (12);
 };
 
+template <typename KernSubTableHeader>
 struct KerxSubTableFormat0
 {
   inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right,
@@ -141,13 +142,14 @@ struct KerxSubTableFormat0
   }
 
   protected:
-  KerxSubTableHeader	header;
+  KernSubTableHeader	header;
   BinSearchArrayOf<KernPair, HBUINT32>
 			pairs;	/* Sorted kern records. */
   public:
-  DEFINE_SIZE_ARRAY (28, pairs);
+  DEFINE_SIZE_ARRAY (KernSubTableHeader::static_size + 16, pairs);
 };
 
+template <typename KernSubTableHeader>
 struct KerxSubTableFormat1
 {
   struct EntryData
@@ -329,13 +331,14 @@ struct KerxSubTableFormat1
   }
 
   protected:
-  KerxSubTableHeader				header;
+  KernSubTableHeader				header;
   StateTable<MorxTypes, EntryData>		machine;
   LOffsetTo<UnsizedArrayOf<FWORD>, false>	kernAction;
   public:
-  DEFINE_SIZE_STATIC (32);
+  DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 20);
 };
 
+template <typename KernSubTableHeader>
 struct KerxSubTableFormat2
 {
   inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right,
@@ -390,7 +393,7 @@ struct KerxSubTableFormat2
   }
 
   protected:
-  KerxSubTableHeader	header;
+  KernSubTableHeader	header;
   HBUINT32		rowWidth;	/* The width, in bytes, of a row in the table. */
   LOffsetTo<Lookup<HBUINT16>, false>
 			leftClassTable;	/* Offset from beginning of this subtable to
@@ -402,9 +405,10 @@ struct KerxSubTableFormat2
 			 array;		/* Offset from beginning of this subtable to
 					 * the start of the kerning array. */
   public:
-  DEFINE_SIZE_STATIC (28);
+  DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 16);
 };
 
+template <typename KernSubTableHeader>
 struct KerxSubTableFormat4
 {
   struct EntryData
@@ -566,14 +570,15 @@ struct KerxSubTableFormat4
   }
 
   protected:
-  KerxSubTableHeader	header;
+  KernSubTableHeader	header;
   StateTable<MorxTypes, EntryData>
 			machine;
   HBUINT32		flags;
   public:
-  DEFINE_SIZE_STATIC (32);
+  DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 20);
 };
 
+template <typename KernSubTableHeader>
 struct KerxSubTableFormat6
 {
   enum Flags
@@ -589,7 +594,7 @@ struct KerxSubTableFormat6
     unsigned int num_glyphs = c->sanitizer.get_num_glyphs ();
     if (is_long ())
     {
-      const U::Long &t = u.l;
+      const typename U::Long &t = u.l;
       unsigned int l = (this+t.rowIndexTable).get_value_or_null (left, num_glyphs);
       unsigned int r = (this+t.columnIndexTable).get_value_or_null (right, num_glyphs);
       unsigned int offset = l + r;
@@ -601,7 +606,7 @@ struct KerxSubTableFormat6
     }
     else
     {
-      const U::Short &t = u.s;
+      const typename U::Short &t = u.s;
       unsigned int l = (this+t.rowIndexTable).get_value_or_null (left, num_glyphs);
       unsigned int r = (this+t.columnIndexTable).get_value_or_null (right, num_glyphs);
       unsigned int offset = l + r;
@@ -660,7 +665,7 @@ struct KerxSubTableFormat6
   };
 
   protected:
-  KerxSubTableHeader		header;
+  KernSubTableHeader		header;
   HBUINT32			flags;
   HBUINT16			rowCount;
   HBUINT16			columnCount;
@@ -681,7 +686,7 @@ struct KerxSubTableFormat6
   } u;
   LOffsetTo<UnsizedArrayOf<FWORD>, false>	vector;
   public:
-  DEFINE_SIZE_STATIC (36);
+  DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 24);
 };
 
 struct KerxTable
@@ -718,12 +723,12 @@ struct KerxTable
 
 protected:
   union {
-  KerxSubTableHeader	header;
-  KerxSubTableFormat0	format0;
-  KerxSubTableFormat1	format1;
-  KerxSubTableFormat2	format2;
-  KerxSubTableFormat4	format4;
-  KerxSubTableFormat6	format6;
+  KerxSubTableHeader				header;
+  KerxSubTableFormat0<KerxSubTableHeader>	format0;
+  KerxSubTableFormat1<KerxSubTableHeader>	format1;
+  KerxSubTableFormat2<KerxSubTableHeader>	format2;
+  KerxSubTableFormat4<KerxSubTableHeader>	format4;
+  KerxSubTableFormat6<KerxSubTableHeader>	format6;
   } u;
 public:
   DEFINE_SIZE_MIN (12);
commit befac337ca2c705e2cea60a9a92e40e0dbbc40aa
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 09:53:02 2018 -0500

    [kern] Remove Override business
    
    Not used in any fonts.  Not well-specified when mixing kerning with
    Cross-Stream positioning.

diff --git a/src/hb-ot-kern-table.hh b/src/hb-ot-kern-table.hh
index b8006005..33e68271 100644
--- a/src/hb-ot-kern-table.hh
+++ b/src/hb-ot-kern-table.hh
@@ -588,8 +588,6 @@ struct KernTable
 	   (st->u.header.Variation | st->u.header.CrossStream | st->u.header.Direction)) !=
 	  st->u.header.DirectionHorizontal)
         continue;
-      if (st->u.header.coverage & st->u.header.Override)
-        v = 0;
       v += st->get_kerning (left, right);
       st = &StructAfter<SubTable> (*st);
     }
@@ -603,15 +601,6 @@ struct KernTable
     c->set_lookup_index (0);
     const SubTable *st = CastP<SubTable> (&thiz()->dataZ);
     unsigned int count = thiz()->nTables;
-    /* If there's an override subtable, skip subtables before that. */
-    unsigned int last_override = 0;
-    for (unsigned int i = 0; i < count; i++)
-    {
-      if (!(st->u.header.coverage & (st->u.header.Variation | st->u.header.CrossStream)) &&
-	  (st->u.header.coverage & st->u.header.Override))
-        last_override = i;
-      st = &StructAfter<SubTable> (*st);
-    }
     st = CastP<SubTable> (&thiz()->dataZ);
     for (unsigned int i = 0; i < count; i++)
     {
@@ -622,9 +611,6 @@ struct KernTable
 	  ((st->u.header.coverage & st->u.header.Direction) == st->u.header.DirectionHorizontal))
 	goto skip;
 
-      if (i < last_override)
-	goto skip;
-
       if (!c->buffer->message (c->font, "start kern subtable %d", c->lookup_index))
 	goto skip;
 
@@ -719,8 +705,6 @@ struct KernAAT : KernTable<KernAAT>
       CrossStream	= 0x40u,
       Variation		= 0x20u,
 
-      Override		= 0x00u, /* Not supported. */
-
       DirectionHorizontal= 0x00u
     };
 
commit 59e04e42312293c30714a666c4479e209aec3c0e
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 00:25:48 2018 -0500

    [kern/kerx] Fix cursive joining
    
    Tested with Waseem TTC:
    
    $ hb-shape Waseem.ttc جحخج
    [F1Jeem_R2=3 at 0,180+479|M1Khah_L2_R2=2 at 0,682+403|M1Hah_L2_R2=1 at 0,1184+403|I1Jeem_L2=0 at 0,1184+744]

diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh
index efbe7ed3..6f63aa40 100644
--- a/src/hb-aat-layout-kerx-table.hh
+++ b/src/hb-aat-layout-kerx-table.hh
@@ -284,6 +284,8 @@ struct KerxSubTableFormat1
 	}
 	depth = 0;
       }
+      else
+	buffer->pos[buffer->idx].y_offset += c->font->em_scale_y (crossOffset);
 
       return true;
     }
diff --git a/src/hb-ot-kern-table.hh b/src/hb-ot-kern-table.hh
index ec7ce929..b8006005 100644
--- a/src/hb-ot-kern-table.hh
+++ b/src/hb-ot-kern-table.hh
@@ -334,6 +334,8 @@ struct KernSubTableFormat1
 	}
 	depth = 0;
       }
+      else
+	buffer->pos[buffer->idx].y_offset += c->font->em_scale_y (crossOffset);
 
       return true;
     }
commit 8d0f797139e853d13cb2383d541c2e691d9dbae3
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Nov 7 00:04:40 2018 -0500

    [kern/kerx] Fix "reset" magic value

diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh
index 93d3ffb7..efbe7ed3 100644
--- a/src/hb-aat-layout-kerx-table.hh
+++ b/src/hb-aat-layout-kerx-table.hh
@@ -238,7 +238,7 @@ struct KerxSubTableFormat1
 
 	  /* The following flag is undocumented in the spec, but described
 	   * in the 'kern' table example. */
-	  if (v == 0x8000)
+	  if (v == -0x8000)
 	  {
 	    crossOffset = 0;
 	    v = 0;
diff --git a/src/hb-ot-kern-table.hh b/src/hb-ot-kern-table.hh
index 28bca843..ec7ce929 100644
--- a/src/hb-ot-kern-table.hh
+++ b/src/hb-ot-kern-table.hh
@@ -287,7 +287,7 @@ struct KernSubTableFormat1
 
 	  /* The following flag is undocumented in the spec, but described
 	   * in the example. */
-	  if (v == 0x8000)
+	  if (v == -0x8000)
 	  {
 	    crossOffset = 0;
 	    v = 0;
commit 0123976a0c1e2f629252969a7ff632dc2b1dbbc9
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Tue Nov 6 21:45:40 2018 -0500

    [kerx] Adjust CrossStream kern to match 'kern' table

diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh
index 550abf31..93d3ffb7 100644
--- a/src/hb-aat-layout-kerx-table.hh
+++ b/src/hb-aat-layout-kerx-table.hh
@@ -179,7 +179,8 @@ struct KerxSubTableFormat1
 	 * other subtables in kerx.  Discovered via testing. */
 	kernAction (&table->machine + table->kernAction),
 	depth (0),
-	crossStream (table->header.coverage & table->header.CrossStream) {}
+	crossStream (table->header.coverage & table->header.CrossStream),
+	crossOffset (0) {}
 
     inline bool is_actionable (StateTableDriver<MorxTypes, EntryData> *driver HB_UNUSED,
 			       const Entry<EntryData> *entry)
@@ -215,36 +216,68 @@ struct KerxSubTableFormat1
 	}
 
 	hb_mask_t kern_mask = c->plan->kern_mask;
-	for (unsigned int i = 0; i < depth; i++)
+
+	/* From Apple 'kern' spec:
+	 * "Each pops one glyph from the kerning stack and applies the kerning value to it.
+	 * The end of the list is marked by an odd value... */
+	unsigned int i;
+	for (i = 0; i < depth; i++)
+	  if (actions[i] & 1)
+	  {
+	    i++;
+	    break;
+	  }
+	for (; i; i--)
 	{
-	  /* Apparently, when spec says "Each pops one glyph from the kerning stack
-	   * and applies the kerning value to it.", it doesn't mean it in that order.
-	   * The deepest item in the stack corresponds to the first item in the action
-	   * list.  Discovered by testing. */
-	  unsigned int idx = stack[i];
-	  int v = *actions++;
+	  unsigned int idx = stack[depth - i];
+	  int v = actions[i - 1];
+
+	  /* "The end of the list is marked by an odd value..."
+	   * Ignore it. */
+	  v &= ~1;
+
+	  /* The following flag is undocumented in the spec, but described
+	   * in the 'kern' table example. */
+	  if (v == 0x8000)
+	  {
+	    crossOffset = 0;
+	    v = 0;
+	  }
 	  if (idx < buffer->len && buffer->info[idx].mask & kern_mask)
 	  {
 	    if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
 	    {
 	      if (crossStream)
-		buffer->pos[idx].y_offset += c->font->em_scale_y (v);
+	      {
+		crossOffset += v;
+		if (!buffer->pos[idx].y_offset)
+		  buffer->pos[idx].y_offset += c->font->em_scale_y (crossOffset);
+	      }
 	      else
 	      {
-		buffer->pos[idx].x_advance += c->font->em_scale_x (v);
-		if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction))
+		if (!buffer->pos[idx].x_offset)
+		{
+		  buffer->pos[idx].x_advance += c->font->em_scale_x (v);
 		  buffer->pos[idx].x_offset += c->font->em_scale_x (v);
+		}
 	      }
 	    }
 	    else
 	    {
 	      if (crossStream)
-		buffer->pos[idx].x_offset += c->font->em_scale_x (v);
+	      {
+	        /* CoreText doesn't do crossStream kerning in vertical. */
+		//crossOffset += v;
+		//if (!buffer->pos[idx].x_offset)
+		//  buffer->pos[idx].x_offset = c->font->em_scale_x (crossOffset);
+	      }
 	      else
 	      {
-		buffer->pos[idx].y_advance += c->font->em_scale_y (v);
-		if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction))
+		if (!buffer->pos[idx].y_offset)
+		{
+		  buffer->pos[idx].y_advance += c->font->em_scale_y (v);
 		  buffer->pos[idx].y_offset += c->font->em_scale_y (v);
+		}
 	      }
 	    }
 	  }
@@ -261,6 +294,7 @@ struct KerxSubTableFormat1
     unsigned int stack[8];
     unsigned int depth;
     bool crossStream;
+    int crossOffset;
   };
 
   inline bool apply (hb_aat_apply_context_t *c) const
commit 80a33b9ac351d81793f35a92e0255ffbf5ceb8b9
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Tue Nov 6 21:41:28 2018 -0500

    [kern] More tweaks
    
    Solves a mystery or two.  I'm fairly confident this is what CoreText does now.

diff --git a/src/hb-ot-kern-table.hh b/src/hb-ot-kern-table.hh
index 2dccc40e..28bca843 100644
--- a/src/hb-ot-kern-table.hh
+++ b/src/hb-ot-kern-table.hh
@@ -266,15 +266,27 @@ struct KernSubTableFormat1
 	}
 
 	hb_mask_t kern_mask = c->plan->kern_mask;
-	while (depth--)
+
+	/* "Each pops one glyph from the kerning stack and applies the kerning value to it.
+	 * The end of the list is marked by an odd value... */
+	unsigned int i;
+	for (i = 0; i < depth; i++)
+	  if (actions[i] & 1)
+	  {
+	    i++;
+	    break;
+	  }
+	for (; i; i--)
 	{
-	  unsigned int idx = stack[depth];
-	  int v = *actions++;
+	  unsigned int idx = stack[depth - i];
+	  int v = actions[i - 1];
 
-	  /* The following two flags are undocumented in the spec, but described
-	   * in the example. */
-	  bool last = v & 1;
+	  /* "The end of the list is marked by an odd value..."
+	   * Ignore it. */
 	  v &= ~1;
+
+	  /* The following flag is undocumented in the spec, but described
+	   * in the example. */
 	  if (v == 0x8000)
 	  {
 	    crossOffset = 0;
@@ -287,11 +299,9 @@ struct KernSubTableFormat1
 	    {
 	      if (crossStream)
 	      {
+		crossOffset += v;
 		if (!buffer->pos[idx].y_offset)
 		  buffer->pos[idx].y_offset += c->font->em_scale_y (crossOffset);
-	        /* XXX Why negative, not positive?!?! */
-	        v = -v;
-		crossOffset += v;
 	      }
 	      else
 	      {
@@ -307,13 +317,9 @@ struct KernSubTableFormat1
 	      if (crossStream)
 	      {
 	        /* CoreText doesn't do crossStream kerning in vertical. */
-#if 0
-		if (!buffer->pos[idx].x_offset)
-		  buffer->pos[idx].x_offset = c->font->em_scale_x (crossOffset);
-#endif
-	        /* XXX Why negative, not positive?!?! */
-	        v = -v;
-		crossOffset += v;
+		//crossOffset += v;
+		//if (!buffer->pos[idx].x_offset)
+		//  buffer->pos[idx].x_offset = c->font->em_scale_x (crossOffset);
 	      }
 	      else
 	      {
@@ -325,8 +331,6 @@ struct KernSubTableFormat1
 	      }
 	    }
 	  }
-	  if (last)
-	    break;
 	}
 	depth = 0;
       }
commit 564e8ac0465d8ced3a98ecb55d09ffaa45eefc2f
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Tue Nov 6 21:04:40 2018 -0500

    [kern] Adjust some more
    
    Getting closer.  So many open questions still...

diff --git a/src/hb-ot-kern-table.hh b/src/hb-ot-kern-table.hh
index bce0aa3e..2dccc40e 100644
--- a/src/hb-ot-kern-table.hh
+++ b/src/hb-ot-kern-table.hh
@@ -266,13 +266,9 @@ struct KernSubTableFormat1
 	}
 
 	hb_mask_t kern_mask = c->plan->kern_mask;
-	for (unsigned int i = 0; i < depth; i++)
+	while (depth--)
 	{
-	  /* Apparently, when spec says "Each pops one glyph from the kerning stack
-	   * and applies the kerning value to it.", it doesn't mean it in that order.
-	   * The deepest item in the stack corresponds to the first item in the action
-	   * list.  Discovered by testing. */
-	  unsigned int idx = stack[i];
+	  unsigned int idx = stack[depth];
 	  int v = *actions++;
 
 	  /* The following two flags are undocumented in the spec, but described
@@ -291,29 +287,41 @@ struct KernSubTableFormat1
 	    {
 	      if (crossStream)
 	      {
+		if (!buffer->pos[idx].y_offset)
+		  buffer->pos[idx].y_offset += c->font->em_scale_y (crossOffset);
 	        /* XXX Why negative, not positive?!?! */
-	        crossOffset -= v;
-		buffer->pos[idx].y_offset += c->font->em_scale_y (crossOffset);
+	        v = -v;
+		crossOffset += v;
 	      }
 	      else
 	      {
-		buffer->pos[idx].x_advance += c->font->em_scale_x (v);
-		if (last)
+		if (!buffer->pos[idx].x_offset)
+		{
+		  buffer->pos[idx].x_advance += c->font->em_scale_x (v);
 		  buffer->pos[idx].x_offset += c->font->em_scale_x (v);
+		}
 	      }
 	    }
 	    else
 	    {
 	      if (crossStream)
 	      {
-	        crossOffset += v;
-		buffer->pos[idx].x_offset += c->font->em_scale_x (crossOffset);
+	        /* CoreText doesn't do crossStream kerning in vertical. */
+#if 0
+		if (!buffer->pos[idx].x_offset)
+		  buffer->pos[idx].x_offset = c->font->em_scale_x (crossOffset);
+#endif
+	        /* XXX Why negative, not positive?!?! */
+	        v = -v;
+		crossOffset += v;
 	      }
 	      else
 	      {
-		buffer->pos[idx].y_advance += c->font->em_scale_y (v);
-		if (last)
+		if (!buffer->pos[idx].y_offset)
+		{
+		  buffer->pos[idx].y_advance += c->font->em_scale_y (v);
 		  buffer->pos[idx].y_offset += c->font->em_scale_y (v);
+		}
 	      }
 	    }
 	  }
commit 4d003b8503f9c984abe2ac0de8c526a276ea8e54
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Tue Nov 6 21:04:02 2018 -0500

    [kern] Add TODO

diff --git a/src/hb-ot-kern-table.hh b/src/hb-ot-kern-table.hh
index 0e60c582..bce0aa3e 100644
--- a/src/hb-ot-kern-table.hh
+++ b/src/hb-ot-kern-table.hh
@@ -225,6 +225,15 @@ struct KernSubTableFormat1
 	crossStream (table->header.coverage & table->header.CrossStream),
 	crossOffset (0) {}
 
+    /* TODO
+     *
+     * "Because the stateTableOffset in the state table header is (strictly
+     * speaking) redundant, some 'kern' tables use it to record an initial
+     * state where that should not be StartOfText. To determine if this is
+     * done, calculate what the stateTableOffset should be. If it's different
+     * from the actual stateTableOffset, use it as the initial state."
+     */
+
     inline bool is_actionable (AAT::StateTableDriver<AAT::MortTypes, EntryData> *driver HB_UNUSED,
 			       const AAT::Entry<EntryData> *entry)
     {
commit 9810f0b80e5b6580a7a15debcec073dfc9ca759f
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Tue Nov 6 19:24:04 2018 -0500

    [kern] Minor

diff --git a/src/hb-ot-kern-table.hh b/src/hb-ot-kern-table.hh
index 980b7e15..0e60c582 100644
--- a/src/hb-ot-kern-table.hh
+++ b/src/hb-ot-kern-table.hh
@@ -269,7 +269,7 @@ struct KernSubTableFormat1
 	  /* The following two flags are undocumented in the spec, but described
 	   * in the example. */
 	  bool last = v & 1;
-	  v = v & ~1;
+	  v &= ~1;
 	  if (v == 0x8000)
 	  {
 	    crossOffset = 0;
commit 9c04b6058306cd4b2123a33a7cbeb47505434217
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Tue Nov 6 18:35:58 2018 -0500

    [kern] In Format1, adjust how kerning is done
    
    In a series of kerns in one action, kern all but last glyph forward,
    and the last one backward.  Seems to better match what CoreText is doing.
    
    Test cases, with GeezaPro Arabic:
    
    $ ./hb-shape GeezaPro_10_10.ttc -u U+0631,U+0628
    [u0628.beh=1+1415|u0631.reh=0 at -202,0+700]
    
    $ ./hb-shape GeezaPro_10_10.ttc -u U+0628,U+064F
    [u064f.damma=0 at 0,-250+-250|u0628.beh=0 at 250,0+1665]
    
    In a later change, I'll make kern machine avoid producing negative kerns.

diff --git a/src/hb-ot-kern-table.hh b/src/hb-ot-kern-table.hh
index c7282a3a..980b7e15 100644
--- a/src/hb-ot-kern-table.hh
+++ b/src/hb-ot-kern-table.hh
@@ -289,7 +289,7 @@ struct KernSubTableFormat1
 	      else
 	      {
 		buffer->pos[idx].x_advance += c->font->em_scale_x (v);
-		if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction))
+		if (last)
 		  buffer->pos[idx].x_offset += c->font->em_scale_x (v);
 	      }
 	    }
@@ -303,7 +303,7 @@ struct KernSubTableFormat1
 	      else
 	      {
 		buffer->pos[idx].y_advance += c->font->em_scale_y (v);
-		if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction))
+		if (last)
 		  buffer->pos[idx].y_offset += c->font->em_scale_y (v);
 	      }
 	    }
commit e8c47724638c29d78001905610c662de99c59cad
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Tue Nov 6 17:16:04 2018 -0500

    [kern] XXX Negate CrossKerning sign
    
    Not sure why, but seems to better match GeezaPro Arabic w CoreText.
    
    Quite possibly I'm doing something very wrong...

diff --git a/src/hb-ot-kern-table.hh b/src/hb-ot-kern-table.hh
index 30f7091b..c7282a3a 100644
--- a/src/hb-ot-kern-table.hh
+++ b/src/hb-ot-kern-table.hh
@@ -282,7 +282,8 @@ struct KernSubTableFormat1
 	    {
 	      if (crossStream)
 	      {
-	        crossOffset += v;
+	        /* XXX Why negative, not positive?!?! */
+	        crossOffset -= v;
 		buffer->pos[idx].y_offset += c->font->em_scale_y (crossOffset);
 	      }
 	      else
commit 01bf43ac01576a6415336cc56c74bb1a872566d1
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Tue Nov 6 14:48:42 2018 -0500

    [kern] Implement CrossStream kerning

diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh
index 0c9a0ffc..550abf31 100644
--- a/src/hb-aat-layout-kerx-table.hh
+++ b/src/hb-aat-layout-kerx-table.hh
@@ -62,6 +62,20 @@ kerxTupleKern (int value,
 
 struct KerxSubTableHeader
 {
+  enum Coverage
+  {
+    Vertical		= 0x80000000,	/* Set if table has vertical kerning values. */
+    CrossStream		= 0x40000000,	/* Set if table has cross-stream kerning values. */
+    Variation		= 0x20000000,	/* Set if table has variation kerning values. */
+    Backwards		= 0x10000000,	/* If clear, process the glyphs forwards, that
+					 * is, from first to last in the glyph stream.
+					 * If we, process them from last to first.
+					 * This flag only applies to state-table based
+					 * 'kerx' subtables (types 1 and 4). */
+    Reserved		= 0x0FFFFF00,	/* Reserved, set to zero. */
+    SubtableType	= 0x000000FF,	/* Subtable type. */
+  };
+
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
@@ -95,6 +109,9 @@ struct KerxSubTableFormat0
     if (!c->plan->requested_kerning)
       return false;
 
+    if (header.coverage & header.CrossStream)
+      return false;
+
     accelerator_t accel (*this, c);
     hb_kern_machine_t<accelerator_t> machine (accel);
     machine.kern (c->font, c->buffer, c->plan->kern_mask);
@@ -161,7 +178,8 @@ struct KerxSubTableFormat1
 	 * similar to offsets in morx table, NOT from beginning of this table, like
 	 * other subtables in kerx.  Discovered via testing. */
 	kernAction (&table->machine + table->kernAction),
-	depth (0) {}
+	depth (0),
+	crossStream (table->header.coverage & table->header.CrossStream) {}
 
     inline bool is_actionable (StateTableDriver<MorxTypes, EntryData> *driver HB_UNUSED,
 			       const Entry<EntryData> *entry)
@@ -209,15 +227,25 @@ struct KerxSubTableFormat1
 	  {
 	    if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
 	    {
-	      buffer->pos[idx].x_advance += c->font->em_scale_x (v);
-	      if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction))
-		buffer->pos[idx].x_offset += c->font->em_scale_x (v);
+	      if (crossStream)
+		buffer->pos[idx].y_offset += c->font->em_scale_y (v);
+	      else
+	      {
+		buffer->pos[idx].x_advance += c->font->em_scale_x (v);
+		if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction))
+		  buffer->pos[idx].x_offset += c->font->em_scale_x (v);
+	      }
 	    }
 	    else
 	    {
-	      buffer->pos[idx].y_advance += c->font->em_scale_y (v);
-	      if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction))
-		buffer->pos[idx].y_offset += c->font->em_scale_y (v);
+	      if (crossStream)
+		buffer->pos[idx].x_offset += c->font->em_scale_x (v);
+	      else
+	      {
+		buffer->pos[idx].y_advance += c->font->em_scale_y (v);
+		if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction))
+		  buffer->pos[idx].y_offset += c->font->em_scale_y (v);
+	      }
 	    }
 	  }
 	}
@@ -232,6 +260,7 @@ struct KerxSubTableFormat1
     const UnsizedArrayOf<FWORD> &kernAction;
     unsigned int stack[8];
     unsigned int depth;
+    bool crossStream;
   };
 
   inline bool apply (hb_aat_apply_context_t *c) const
@@ -241,6 +270,9 @@ struct KerxSubTableFormat1
     if (!c->plan->requested_kerning)
       return false;
 
+    if (header.coverage & header.CrossStream)
+      return false;
+
     if (header.tupleCount)
       return_trace (false); /* TODO kerxTupleKern */
 
@@ -289,6 +321,9 @@ struct KerxSubTableFormat2
     if (!c->plan->requested_kerning)
       return false;
 
+    if (header.coverage & header.CrossStream)
+      return false;
+
     accelerator_t accel (*this, c);
     hb_kern_machine_t<accelerator_t> machine (accel);
     machine.kern (c->font, c->buffer, c->plan->kern_mask);
@@ -547,6 +582,9 @@ struct KerxSubTableFormat6
     if (!c->plan->requested_kerning)
       return false;
 
+    if (header.coverage & header.CrossStream)
+      return false;
+
     accelerator_t accel (*this, c);
     hb_kern_machine_t<accelerator_t> machine (accel);
     machine.kern (c->font, c->buffer, c->plan->kern_mask);
@@ -615,21 +653,7 @@ struct KerxTable
   friend struct kerx;
 
   inline unsigned int get_size (void) const { return u.header.length; }
-  inline unsigned int get_type (void) const { return u.header.coverage & SubtableType; }
-
-  enum Coverage
-  {
-    Vertical		= 0x80000000,	/* Set if table has vertical kerning values. */
-    CrossStream		= 0x40000000,	/* Set if table has cross-stream kerning values. */
-    Variation		= 0x20000000,	/* Set if table has variation kerning values. */
-    Backwards		= 0x10000000,	/* If clear, process the glyphs forwards, that
-					 * is, from first to last in the glyph stream.
-					 * If we, process them from last to first.
-					 * This flag only applies to state-table based
-					 * 'kerx' subtables (types 1 and 4). */
-    Reserved		= 0x0FFFFF00,	/* Reserved, set to zero. */
-    SubtableType	= 0x000000FF,	/* Subtable type. */
-  };
+  inline unsigned int get_type (void) const { return u.header.coverage & u.header.SubtableType; }
 
   template <typename context_t>
   inline typename context_t::return_t dispatch (context_t *c) const
@@ -689,14 +713,11 @@ struct kerx
     {
       bool reverse;
 
-      if (table->u.header.coverage & (KerxTable::CrossStream))
-	goto skip; /* We do NOT handle cross-stream. */
-
       if (HB_DIRECTION_IS_VERTICAL (c->buffer->props.direction) !=
-	  bool (table->u.header.coverage & KerxTable::Vertical))
+	  bool (table->u.header.coverage & table->u.header.Vertical))
 	goto skip;
 
-      reverse = bool (table->u.header.coverage & KerxTable::Backwards) !=
+      reverse = bool (table->u.header.coverage & table->u.header.Backwards) !=
 		HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction);
 
       if (!c->buffer->message (c->font, "start kerx subtable %d", c->lookup_index))
diff --git a/src/hb-ot-kern-table.hh b/src/hb-ot-kern-table.hh
index c54c1008..30f7091b 100644
--- a/src/hb-ot-kern-table.hh
+++ b/src/hb-ot-kern-table.hh
@@ -174,6 +174,9 @@ struct KernSubTableFormat0
     if (!c->plan->requested_kerning)
       return false;
 
+    if (header.coverage & header.CrossStream)
+      return false;
+
     hb_kern_machine_t<KernSubTableFormat0> machine (*this);
     machine.kern (c->font, c->buffer, c->plan->kern_mask);
 
@@ -218,7 +221,9 @@ struct KernSubTableFormat1
 	 * similar to offsets in morx table, NOT from beginning of this table, like
 	 * other subtables in kerx.  Discovered via testing. */
 	kernAction (&table->machine + table->kernAction),
-	depth (0) {}
+	depth (0),
+	crossStream (table->header.coverage & table->header.CrossStream),
+	crossOffset (0) {}
 
     inline bool is_actionable (AAT::StateTableDriver<AAT::MortTypes, EntryData> *driver HB_UNUSED,
 			       const AAT::Entry<EntryData> *entry)
@@ -241,7 +246,9 @@ struct KernSubTableFormat1
 
       if (entry->flags & Offset)
       {
-	unsigned int kernIndex = AAT::MortTypes::offsetToIndex (entry->flags & Offset, &table->machine, kernAction.arrayZ);
+	unsigned int kernIndex = AAT::MortTypes::offsetToIndex (entry->flags & Offset,
+								&table->machine,
+								kernAction.arrayZ);
 	const FWORD *actions = &kernAction[kernIndex];
 	if (!c->sanitizer.check_array (actions, depth))
 	{
@@ -258,21 +265,50 @@ struct KernSubTableFormat1
 	   * list.  Discovered by testing. */
 	  unsigned int idx = stack[i];
 	  int v = *actions++;
+
+	  /* The following two flags are undocumented in the spec, but described
+	   * in the example. */
+	  bool last = v & 1;
+	  v = v & ~1;
+	  if (v == 0x8000)
+	  {
+	    crossOffset = 0;
+	    v = 0;
+	  }
+
 	  if (idx < buffer->len && buffer->info[idx].mask & kern_mask)
 	  {
 	    if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
 	    {
-	      buffer->pos[idx].x_advance += c->font->em_scale_x (v);
-	      if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction))
-		buffer->pos[idx].x_offset += c->font->em_scale_x (v);
+	      if (crossStream)
+	      {
+	        crossOffset += v;
+		buffer->pos[idx].y_offset += c->font->em_scale_y (crossOffset);
+	      }
+	      else
+	      {
+		buffer->pos[idx].x_advance += c->font->em_scale_x (v);
+		if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction))
+		  buffer->pos[idx].x_offset += c->font->em_scale_x (v);
+	      }
 	    }
 	    else
 	    {
-	      buffer->pos[idx].y_advance += c->font->em_scale_y (v);
-	      if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction))
-		buffer->pos[idx].y_offset += c->font->em_scale_y (v);
+	      if (crossStream)
+	      {
+	        crossOffset += v;
+		buffer->pos[idx].x_offset += c->font->em_scale_x (crossOffset);
+	      }
+	      else
+	      {
+		buffer->pos[idx].y_advance += c->font->em_scale_y (v);
+		if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction))
+		  buffer->pos[idx].y_offset += c->font->em_scale_y (v);
+	      }
 	    }
 	  }
+	  if (last)
+	    break;
 	}
 	depth = 0;
       }
@@ -286,6 +322,8 @@ struct KernSubTableFormat1
     const UnsizedArrayOf<FWORD> &kernAction;
     unsigned int stack[8];
     unsigned int depth;
+    bool crossStream;
+    int crossOffset;
   };
 
   inline bool apply (AAT::hb_aat_apply_context_t *c) const
@@ -344,6 +382,9 @@ struct KernSubTableFormat2
     if (!c->plan->requested_kerning)
       return false;
 
+    if (header.coverage & header.CrossStream)
+      return false;
+
     accelerator_t accel (*this, c);
     hb_kern_machine_t<accelerator_t> machine (accel);
     machine.kern (c->font, c->buffer, c->plan->kern_mask);
@@ -412,6 +453,9 @@ struct KernSubTableFormat3
     if (!c->plan->requested_kerning)
       return false;
 
+    if (header.coverage & header.CrossStream)
+      return false;
+
     hb_kern_machine_t<KernSubTableFormat3> machine (*this);
     machine.kern (c->font, c->buffer, c->plan->kern_mask);
 
@@ -547,8 +591,7 @@ struct KernTable
     st = CastP<SubTable> (&thiz()->dataZ);
     for (unsigned int i = 0; i < count; i++)
     {
-      if (st->u.header.coverage &
-	  (st->u.header.Variation | st->u.header.CrossStream))
+      if (st->u.header.coverage & st->u.header.Variation)
         goto skip;
 
       if (HB_DIRECTION_IS_HORIZONTAL (c->buffer->props.direction) !=
commit b11830c09e0d78bbdaf86ef02191d00b3d8256c4
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Tue Nov 6 15:23:18 2018 -0500

    [kern] Improve Format 2
    
    Still disabled.

diff --git a/src/hb-aat-layout-common.hh b/src/hb-aat-layout-common.hh
index ddfd04b8..539941d8 100644
--- a/src/hb-aat-layout-common.hh
+++ b/src/hb-aat-layout-common.hh
@@ -528,24 +528,22 @@ struct StateTable
 
 struct ClassTable
 {
-  inline unsigned int get_class (hb_codepoint_t glyph_id) const
+  inline unsigned int get_class (hb_codepoint_t glyph_id, unsigned int outOfRange=0) const
   {
-    return firstGlyph <= glyph_id && glyph_id - firstGlyph < glyphCount ? classArrayZ[glyph_id - firstGlyph] : 1;
+    unsigned int i = glyph_id - firstGlyph;
+    return i >= classArray.len ? outOfRange : classArray.arrayZ[i];
   }
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) && classArrayZ.sanitize (c, glyphCount));
+    return_trace (c->check_struct (this) && classArray.sanitize (c));
   }
   protected:
-  GlyphID	firstGlyph;	/* First glyph index included in the trimmed array. */
-  HBUINT16	glyphCount;	/* Total number of glyphs (equivalent to the last
-				 * glyph minus the value of firstGlyph plus 1). */
-  UnsizedArrayOf<HBUINT8>
-		classArrayZ;	/* The class codes (indexed by glyph index minus
-				 * firstGlyph). */
+  GlyphID		firstGlyph;	/* First glyph index included in the trimmed array. */
+  ArrayOf<HBUINT8>	classArray;	/* The class codes (indexed by glyph index minus
+					 * firstGlyph). */
   public:
-  DEFINE_SIZE_ARRAY (4, classArrayZ);
+  DEFINE_SIZE_ARRAY (4, classArray);
 };
 
 struct MortTypes
@@ -557,7 +555,7 @@ struct MortTypes
   {
     inline unsigned int get_class (hb_codepoint_t glyph_id, unsigned int num_glyphs HB_UNUSED) const
     {
-      return ClassTable::get_class (glyph_id);
+      return ClassTable::get_class (glyph_id, 1);
     }
   };
   template <typename T>
diff --git a/src/hb-ot-kern-table.hh b/src/hb-ot-kern-table.hh
index fa2b91cc..c54c1008 100644
--- a/src/hb-ot-kern-table.hh
+++ b/src/hb-ot-kern-table.hh
@@ -319,44 +319,21 @@ struct KernSubTableFormat1
   DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 10);
 };
 
-struct KernClassTable
-{
-  inline unsigned int get_class (hb_codepoint_t g) const { return classes[g - firstGlyph]; }
-
-  inline bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) &&
-		  classes.sanitize (c));
-  }
-
-  protected:
-  HBUINT16		firstGlyph;	/* First glyph in class range. */
-  ArrayOf<HBUINT16>	classes;	/* Glyph classes. */
-  public:
-  DEFINE_SIZE_ARRAY (4, classes);
-};
-
 template <typename KernSubTableHeader>
 struct KernSubTableFormat2
 {
   inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right,
 			  AAT::hb_aat_apply_context_t *c) const
   {
-    /* This subtable is disabled.  It's not cleaer to me *exactly* where the offests are
-     * based from.  I *think* they should be based from beginning of kern subtable wrapper,
-     * *NOT* "this".  Since we know of no fonts that use this subtable, we are disabling
-     * it.  Someday fix it and re-enable. */
+    /* Disabled until we find a font to test this.  Note that OT vs AAT specify
+     * different ClassTable.  OT's has 16bit entries, while AAT has 8bit entries.
+     * I've not seen any in the wild. */
     return 0;
     unsigned int l = (this+leftClassTable).get_class (left);
     unsigned int r = (this+rightClassTable).get_class (right);
     unsigned int offset = l + r;
     const FWORD *v = &StructAtOffset<FWORD> (&(this+array), offset);
-#if 0
-    if (unlikely ((const char *) v < (const char *) &array ||
-		  (const char *) v > (const char *) end - 2))
-#endif
-      return 0;
+    if (unlikely (!v->sanitize (&c->sanitizer))) return 0;
     return *v;
   }
 
@@ -400,9 +377,9 @@ struct KernSubTableFormat2
   protected:
   KernSubTableHeader		header;
   HBUINT16			rowWidth;	/* The width, in bytes, of a row in the table. */
-  OffsetTo<KernClassTable>	leftClassTable;	/* Offset from beginning of this subtable to
+  OffsetTo<AAT::ClassTable>	leftClassTable;	/* Offset from beginning of this subtable to
 						 * left-hand class table. */
-  OffsetTo<KernClassTable>	rightClassTable;/* Offset from beginning of this subtable to
+  OffsetTo<AAT::ClassTable>	rightClassTable;/* Offset from beginning of this subtable to
 						 * right-hand class table. */
   OffsetTo<FWORD>		array;		/* Offset from beginning of this subtable to
 						 * the start of the kerning array. */
commit c0383c6bb725bed2a48485988a427348384f3f87
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Tue Nov 6 15:07:19 2018 -0500

    Minor

diff --git a/src/hb-aat-layout-common.hh b/src/hb-aat-layout-common.hh
index ee2136ed..ddfd04b8 100644
--- a/src/hb-aat-layout-common.hh
+++ b/src/hb-aat-layout-common.hh
@@ -365,7 +365,7 @@ namespace AAT {
 enum { DELETED_GLYPH = 0xFFFF };
 
 /*
- * Extended State Table
+ * (Extended) State Table
  */
 
 template <typename T>
commit 10e6f708f30986bab9f7b506935f2555d6b79ff4
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Tue Nov 6 13:32:13 2018 -0500

    [kern] Minor

diff --git a/src/hb-ot-kern-table.hh b/src/hb-ot-kern-table.hh
index 82b8b67c..fa2b91cc 100644
--- a/src/hb-ot-kern-table.hh
+++ b/src/hb-ot-kern-table.hh
@@ -478,18 +478,6 @@ struct KernSubTable
   inline unsigned int get_size (void) const { return u.header.length; }
   inline unsigned int get_type (void) const { return u.header.format; }
 
-  inline bool is_crossStream (void) const
-  { return u.header.coverage & u.header.CrossStream; }
-
-  inline bool is_variation (void) const
-  { return u.header.coverage & u.header.Variation; }
-
-  inline bool is_horizontal (void) const
-  { return (u.header.coverage & u.header.Direction) == u.header.DirectionHorizontal; }
-
-  inline bool is_override (void) const
-  { return bool (u.header.coverage & u.header.Override); }
-
   inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
   {
     switch (get_type ()) {
@@ -523,7 +511,7 @@ struct KernSubTable
     return_trace (dispatch (c));
   }
 
-  protected:
+  public:
   union {
   KernSubTableHeader				header;
   KernSubTableFormat0<KernSubTableHeader>	format0;
@@ -551,9 +539,11 @@ struct KernTable
     unsigned int count = thiz()->nTables;
     for (unsigned int i = 0; i < count; i++)
     {
-      if (!st->is_variation () || !st->is_crossStream () || !st->is_horizontal ())
+      if ((st->u.header.coverage &
+	   (st->u.header.Variation | st->u.header.CrossStream | st->u.header.Direction)) !=
+	  st->u.header.DirectionHorizontal)
         continue;
-      if (st->is_override ())
+      if (st->u.header.coverage & st->u.header.Override)
         v = 0;
       v += st->get_kerning (left, right);
       st = &StructAfter<SubTable> (*st);
@@ -572,18 +562,20 @@ struct KernTable
     unsigned int last_override = 0;
     for (unsigned int i = 0; i < count; i++)
     {
-      if (!st->is_variation () && !st->is_crossStream () &&
-	  st->is_override ())
+      if (!(st->u.header.coverage & (st->u.header.Variation | st->u.header.CrossStream)) &&
+	  (st->u.header.coverage & st->u.header.Override))
         last_override = i;
       st = &StructAfter<SubTable> (*st);
     }
     st = CastP<SubTable> (&thiz()->dataZ);
     for (unsigned int i = 0; i < count; i++)
     {
-      if (st->is_variation () || st->is_crossStream ())
+      if (st->u.header.coverage &
+	  (st->u.header.Variation | st->u.header.CrossStream))
         goto skip;
 
-      if (HB_DIRECTION_IS_HORIZONTAL (c->buffer->props.direction) != st->is_horizontal ())
+      if (HB_DIRECTION_IS_HORIZONTAL (c->buffer->props.direction) !=
+	  ((st->u.header.coverage & st->u.header.Direction) == st->u.header.DirectionHorizontal))
 	goto skip;
 
       if (i < last_override)
commit 164eedd9181345d84d5f8059475ad4b97784fd46
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Tue Nov 6 13:18:27 2018 -0500

    [kern] Minor

diff --git a/src/hb-ot-kern-table.hh b/src/hb-ot-kern-table.hh
index 213b9cfc..82b8b67c 100644
--- a/src/hb-ot-kern-table.hh
+++ b/src/hb-ot-kern-table.hh
@@ -478,8 +478,11 @@ struct KernSubTable
   inline unsigned int get_size (void) const { return u.header.length; }
   inline unsigned int get_type (void) const { return u.header.format; }
 
-  inline bool is_simple (void) const
-  { return !(u.header.coverage & (u.header.CrossStream | u.header.Variation)); }
+  inline bool is_crossStream (void) const
+  { return u.header.coverage & u.header.CrossStream; }
+
+  inline bool is_variation (void) const
+  { return u.header.coverage & u.header.Variation; }
 
   inline bool is_horizontal (void) const
   { return (u.header.coverage & u.header.Direction) == u.header.DirectionHorizontal; }
@@ -548,7 +551,7 @@ struct KernTable
     unsigned int count = thiz()->nTables;
     for (unsigned int i = 0; i < count; i++)
     {
-      if (!st->is_simple () || !st->is_horizontal ())
+      if (!st->is_variation () || !st->is_crossStream () || !st->is_horizontal ())
         continue;
       if (st->is_override ())
         v = 0;
@@ -569,14 +572,15 @@ struct KernTable
     unsigned int last_override = 0;
     for (unsigned int i = 0; i < count; i++)
     {
-      if (st->is_simple () && st->is_override ())
+      if (!st->is_variation () && !st->is_crossStream () &&
+	  st->is_override ())
         last_override = i;
       st = &StructAfter<SubTable> (*st);
     }
     st = CastP<SubTable> (&thiz()->dataZ);
     for (unsigned int i = 0; i < count; i++)
     {
-      if (!st->is_simple ())
+      if (st->is_variation () || st->is_crossStream ())
         goto skip;
 
       if (HB_DIRECTION_IS_HORIZONTAL (c->buffer->props.direction) != st->is_horizontal ())
commit 220a5991baa213b7bd173ea02090dc6fc8aef655
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Tue Nov 6 13:51:39 2018 -0500

    [kern/kerx] Fix trace numbering

diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh
index ca2fdb49..0c9a0ffc 100644
--- a/src/hb-aat-layout-kerx-table.hh
+++ b/src/hb-aat-layout-kerx-table.hh
@@ -720,6 +720,7 @@ struct kerx
 
     skip:
       table = &StructAfter<KerxTable> (*table);
+      c->set_lookup_index (c->lookup_index + 1);
     }
   }
 
diff --git a/src/hb-ot-kern-table.hh b/src/hb-ot-kern-table.hh
index 66e827d1..213b9cfc 100644
--- a/src/hb-ot-kern-table.hh
+++ b/src/hb-ot-kern-table.hh
@@ -596,6 +596,7 @@ struct KernTable
 
     skip:
       st = &StructAfter<SubTable> (*st);
+      c->set_lookup_index (c->lookup_index + 1);
     }
   }
 


More information about the HarfBuzz mailing list