[subset] GPOS Lookup Type 3: CursivePos
diff --git a/src/hb-ot-layout-common.hh b/src/hb-ot-layout-common.hh
index 6d349e2..73c28ca 100644
--- a/src/hb-ot-layout-common.hh
+++ b/src/hb-ot-layout-common.hh
@@ -2057,6 +2057,8 @@
   hb_position_t get_y_delta (hb_font_t *font) const
   { return get_delta (font->y_ppem, font->y_scale); }
 
+  public:
+
   unsigned int get_size () const
   {
     unsigned int f = deltaFormat;
@@ -2070,6 +2072,12 @@
     return_trace (c->check_struct (this) && c->check_range (this, this->get_size ()));
   }
 
+  HintingDevice* copy (hb_serialize_context_t *c) const
+  {
+    TRACE_SERIALIZE (this);
+    return_trace (c->embed<HintingDevice> (this));
+  }
+
   private:
 
   int get_delta (unsigned int ppem, int scale) const
@@ -2131,6 +2139,12 @@
   hb_position_t get_y_delta (hb_font_t *font, const VariationStore &store) const
   { return font->em_scalef_y (get_delta (font, store)); }
 
+  VariationDevice* copy (hb_serialize_context_t *c) const
+  {
+    TRACE_SERIALIZE (this);
+    return_trace (c->embed<VariationDevice> (this));
+  }
+
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
@@ -2216,6 +2230,25 @@
     }
   }
 
+  Device* copy (hb_serialize_context_t *c) const
+  {
+    TRACE_SERIALIZE (this);
+    switch (u.b.format) {
+#ifndef HB_NO_HINTING
+    case 1:
+    case 2:
+    case 3:
+      return_trace (reinterpret_cast<Device *> (u.hinting.copy (c)));
+#endif
+#ifndef HB_NO_VAR
+    case 0x8000:
+      return_trace (reinterpret_cast<Device *> (u.variation.copy (c)));
+#endif
+    default:
+      return_trace (nullptr);
+    }
+  }
+
   protected:
   union {
   DeviceHeader		b;
diff --git a/src/hb-ot-layout-gpos-table.hh b/src/hb-ot-layout-gpos-table.hh
index 7b9ccf4..73107f4 100644
--- a/src/hb-ot-layout-gpos-table.hh
+++ b/src/hb-ot-layout-gpos-table.hh
@@ -257,6 +257,12 @@
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this));
   }
+  
+  AnchorFormat1* copy (hb_serialize_context_t *c) const
+  {
+    TRACE_SERIALIZE (this);
+    return_trace (c->embed<AnchorFormat1> (this));
+  }
 
   protected:
   HBUINT16	format;			/* Format identifier--format = 1 */
@@ -296,6 +302,12 @@
     return_trace (c->check_struct (this));
   }
 
+  AnchorFormat2* copy (hb_serialize_context_t *c) const
+  {
+    TRACE_SERIALIZE (this);
+    return_trace (c->embed<AnchorFormat2> (this));
+  }
+
   protected:
   HBUINT16	format;			/* Format identifier--format = 2 */
   FWORD		xCoordinate;		/* Horizontal value--in design units */
@@ -326,6 +338,17 @@
     return_trace (c->check_struct (this) && xDeviceTable.sanitize (c, this) && yDeviceTable.sanitize (c, this));
   }
 
+  AnchorFormat3* copy (hb_serialize_context_t *c) const
+  {
+    TRACE_SERIALIZE (this);
+    auto *out = c->embed<AnchorFormat3> (this);
+    if (unlikely (!out)) return_trace (nullptr);
+
+    out->xDeviceTable.serialize_copy (c, xDeviceTable, this, out);
+    out->yDeviceTable.serialize_copy (c, yDeviceTable, this, out);
+    return_trace (out);
+  }
+
   protected:
   HBUINT16	format;			/* Format identifier--format = 3 */
   FWORD		xCoordinate;		/* Horizontal value--in design units */
@@ -368,6 +391,17 @@
     }
   }
 
+  Anchor* copy (hb_serialize_context_t *c) const
+  {
+    TRACE_SERIALIZE (this);
+    switch (u.format) {
+    case 1: return_trace (reinterpret_cast<Anchor *> (u.format1.copy (c)));
+    case 2: return_trace (reinterpret_cast<Anchor *> (u.format2.copy (c)));
+    case 3: return_trace (reinterpret_cast<Anchor *> (u.format3.copy (c)));
+    default:return_trace (nullptr);
+    }
+  }
+
   protected:
   union {
   HBUINT16		format;		/* Format identifier */
@@ -1064,6 +1098,19 @@
     return_trace (entryAnchor.sanitize (c, base) && exitAnchor.sanitize (c, base));
   }
 
+  EntryExitRecord* copy (hb_serialize_context_t *c,
+			 const void *src_base,
+			 const void *dst_base) const
+  {
+    TRACE_SERIALIZE (this);
+    auto *out = c->embed (this);
+    if (unlikely (!out)) return_trace (nullptr);
+
+    out->entryAnchor.serialize_copy (c, entryAnchor, src_base, dst_base);
+    out->exitAnchor.serialize_copy (c, exitAnchor, src_base, dst_base);
+    return_trace (out);
+  }
+
   protected:
   OffsetTo<Anchor>
 		entryAnchor;		/* Offset to EntryAnchor table--from
@@ -1191,11 +1238,47 @@
     return_trace (true);
   }
 
+  template <typename Iterator,
+	    hb_requires (hb_is_iterator (Iterator))>
+  void serialize (hb_serialize_context_t *c,
+		  Iterator it,
+                  const void *src_base)
+  {
+    if (unlikely (!c->extend_min ((*this)))) return;
+    this->format = 1;
+    this->entryExitRecord.len = it.len ();
+
+    for (const EntryExitRecord& entry_record : + it
+					       | hb_map (hb_second))
+      c->copy (entry_record, src_base, this);
+
+    auto glyphs =
+    + it
+    | hb_map_retains_sorting (hb_first)
+    ;
+
+    coverage.serialize (c, this).serialize (c, glyphs);
+  }
+
   bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
-    // TODO(subset)
-    return_trace (false);
+    const hb_set_t &glyphset = *c->plan->glyphset ();
+    const hb_map_t &glyph_map = *c->plan->glyph_map;
+
+    auto *out = c->serializer->start_embed (*this);
+    if (unlikely (!out)) return_trace (false);
+
+    auto it =
+    + hb_zip (this+coverage, entryExitRecord)
+    | hb_filter (glyphset, hb_first)
+    | hb_map_retains_sorting ([&] (hb_pair_t<hb_codepoint_t, const EntryExitRecord&> p) -> hb_pair_t<hb_codepoint_t, const EntryExitRecord&>
+                              { return hb_pair (glyph_map[p.first], p.second);})
+    ;
+
+    bool ret = bool (it);
+    out->serialize (c->serializer, it, this);
+    return_trace (ret);
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
diff --git a/test/subset/data/Makefile.am b/test/subset/data/Makefile.am
index fb281e0..0b27452 100644
--- a/test/subset/data/Makefile.am
+++ b/test/subset/data/Makefile.am
@@ -14,6 +14,7 @@
 	expected/cff-japanese \
 	expected/layout \
 	expected/layout.gpos \
+	expected/layout.gpos3 \
 	expected/cmap14 \
 	fonts \
 	profiles \
diff --git a/test/subset/data/Makefile.sources b/test/subset/data/Makefile.sources
index 0b2f1a1..c6a3042 100644
--- a/test/subset/data/Makefile.sources
+++ b/test/subset/data/Makefile.sources
@@ -6,6 +6,7 @@
 	tests/cff-japanese.tests \
 	tests/layout.tests \
 	tests/layout.gpos.tests \
+	tests/layout.gpos3.tests \
 	tests/cmap14.tests \
 	$(NULL)
 
diff --git a/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout-retain-gids.28,29.otf b/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout-retain-gids.28,29.otf
new file mode 100644
index 0000000..17aa6d8
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout-retain-gids.28,29.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout-retain-gids.28,2B.otf b/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout-retain-gids.28,2B.otf
new file mode 100644
index 0000000..9e6f2eb
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout-retain-gids.28,2B.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout-retain-gids.29,2B.otf b/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout-retain-gids.29,2B.otf
new file mode 100644
index 0000000..0187ed7
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout-retain-gids.29,2B.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout-retain-gids.retain-all-codepoint.otf b/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout-retain-gids.retain-all-codepoint.otf
new file mode 100644
index 0000000..d9b5dfb
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout-retain-gids.retain-all-codepoint.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout.28,29.otf b/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout.28,29.otf
new file mode 100644
index 0000000..f3ca19a
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout.28,29.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout.28,2B.otf b/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout.28,2B.otf
new file mode 100644
index 0000000..2a8114a
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout.28,2B.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout.29,2B.otf b/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout.29,2B.otf
new file mode 100644
index 0000000..1426d50
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout.29,2B.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout.retain-all-codepoint.otf b/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout.retain-all-codepoint.otf
new file mode 100644
index 0000000..d9b5dfb
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos3/gpos3_font3.keep-layout.retain-all-codepoint.otf
Binary files differ
diff --git a/test/subset/data/fonts/gpos3_font3.otf b/test/subset/data/fonts/gpos3_font3.otf
new file mode 100644
index 0000000..69c74a3
--- /dev/null
+++ b/test/subset/data/fonts/gpos3_font3.otf
Binary files differ
diff --git a/test/subset/data/tests/layout.gpos3.tests b/test/subset/data/tests/layout.gpos3.tests
new file mode 100644
index 0000000..409272f
--- /dev/null
+++ b/test/subset/data/tests/layout.gpos3.tests
@@ -0,0 +1,12 @@
+FONTS:
+gpos3_font3.otf
+
+PROFILES:
+keep-layout.txt
+keep-layout-retain-gids.txt
+
+SUBSETS:
+()
+(+
+)+
+*