diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/Common/Coverage.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/Common/Coverage.hh
new file mode 100644
index 0000000000000000000000000000000000000000..e52a617c86ea214e61e443feb0b76037d58c6166
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/Common/Coverage.hh
@@ -0,0 +1,338 @@
+/*
+ * Copyright © 2007,2008,2009  Red Hat, Inc.
+ * Copyright © 2010,2012  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.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod, Garret Rieger
+ */
+
+#ifndef OT_LAYOUT_COMMON_COVERAGE_HH
+#define OT_LAYOUT_COMMON_COVERAGE_HH
+
+#include "../types.hh"
+#include "CoverageFormat1.hh"
+#include "CoverageFormat2.hh"
+
+namespace OT {
+namespace Layout {
+namespace Common {
+
+template<typename Iterator>
+static inline void Coverage_serialize (hb_serialize_context_t *c,
+                                       Iterator it);
+
+struct Coverage
+{
+
+  protected:
+  union {
+  HBUINT16                      format;         /* Format identifier */
+  CoverageFormat1_3<SmallTypes> format1;
+  CoverageFormat2_4<SmallTypes> format2;
+#ifndef HB_NO_BORING_EXPANSION
+  CoverageFormat1_3<MediumTypes>format3;
+  CoverageFormat2_4<MediumTypes>format4;
+#endif
+  } u;
+  public:
+  DEFINE_SIZE_UNION (2, format);
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    if (!u.format.sanitize (c)) return_trace (false);
+    switch (u.format)
+    {
+    case 1: return_trace (u.format1.sanitize (c));
+    case 2: return_trace (u.format2.sanitize (c));
+#ifndef HB_NO_BORING_EXPANSION
+    case 3: return_trace (u.format3.sanitize (c));
+    case 4: return_trace (u.format4.sanitize (c));
+#endif
+    default:return_trace (true);
+    }
+  }
+
+  /* Has interface. */
+  static constexpr unsigned SENTINEL = NOT_COVERED;
+  typedef unsigned int value_t;
+  value_t operator [] (hb_codepoint_t k) const { return get (k); }
+  bool has (hb_codepoint_t k) const { return (*this)[k] != SENTINEL; }
+  /* Predicate. */
+  bool operator () (hb_codepoint_t k) const { return has (k); }
+
+  unsigned int get (hb_codepoint_t k) const { return get_coverage (k); }
+  unsigned int get_coverage (hb_codepoint_t glyph_id) const
+  {
+    switch (u.format) {
+    case 1: return u.format1.get_coverage (glyph_id);
+    case 2: return u.format2.get_coverage (glyph_id);
+#ifndef HB_NO_BORING_EXPANSION
+    case 3: return u.format3.get_coverage (glyph_id);
+    case 4: return u.format4.get_coverage (glyph_id);
+#endif
+    default:return NOT_COVERED;
+    }
+  }
+
+  unsigned get_population () const
+  {
+    switch (u.format) {
+    case 1: return u.format1.get_population ();
+    case 2: return u.format2.get_population ();
+#ifndef HB_NO_BORING_EXPANSION
+    case 3: return u.format3.get_population ();
+    case 4: return u.format4.get_population ();
+#endif
+    default:return NOT_COVERED;
+    }
+  }
+
+  template <typename Iterator,
+      hb_requires (hb_is_sorted_source_of (Iterator, hb_codepoint_t))>
+  bool serialize (hb_serialize_context_t *c, Iterator glyphs)
+  {
+    TRACE_SERIALIZE (this);
+    if (unlikely (!c->extend_min (this))) return_trace (false);
+
+    unsigned count = 0;
+    unsigned num_ranges = 0;
+    hb_codepoint_t last = (hb_codepoint_t) -2;
+    for (auto g: glyphs)
+    {
+      if (last + 1 != g)
+        num_ranges++;
+      last = g;
+      count++;
+    }
+    u.format = count <= num_ranges * 3 ? 1 : 2;
+
+#ifndef HB_NO_BORING_EXPANSION
+    if (count && last > 0xFFFFu)
+      u.format += 2;
+#endif
+
+    switch (u.format)
+    {
+    case 1: return_trace (u.format1.serialize (c, glyphs));
+    case 2: return_trace (u.format2.serialize (c, glyphs));
+#ifndef HB_NO_BORING_EXPANSION
+    case 3: return_trace (u.format3.serialize (c, glyphs));
+    case 4: return_trace (u.format4.serialize (c, glyphs));
+#endif
+    default:return_trace (false);
+    }
+  }
+
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    auto it =
+    + iter ()
+    | hb_filter (c->plan->glyph_map_gsub)
+    | hb_map_retains_sorting (c->plan->glyph_map_gsub)
+    ;
+
+    // Cache the iterator result as it will be iterated multiple times
+    // by the serialize code below.
+    hb_sorted_vector_t<hb_codepoint_t> glyphs (it);
+    Coverage_serialize (c->serializer, glyphs.iter ());
+    return_trace (bool (glyphs));
+  }
+
+  bool intersects (const hb_set_t *glyphs) const
+  {
+    switch (u.format)
+    {
+    case 1: return u.format1.intersects (glyphs);
+    case 2: return u.format2.intersects (glyphs);
+#ifndef HB_NO_BORING_EXPANSION
+    case 3: return u.format3.intersects (glyphs);
+    case 4: return u.format4.intersects (glyphs);
+#endif
+    default:return false;
+    }
+  }
+  bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const
+  {
+    switch (u.format)
+    {
+    case 1: return u.format1.intersects_coverage (glyphs, index);
+    case 2: return u.format2.intersects_coverage (glyphs, index);
+#ifndef HB_NO_BORING_EXPANSION
+    case 3: return u.format3.intersects_coverage (glyphs, index);
+    case 4: return u.format4.intersects_coverage (glyphs, index);
+#endif
+    default:return false;
+    }
+  }
+
+  /* Might return false if array looks unsorted.
+   * Used for faster rejection of corrupt data. */
+  template <typename set_t>
+  bool collect_coverage (set_t *glyphs) const
+  {
+    switch (u.format)
+    {
+    case 1: return u.format1.collect_coverage (glyphs);
+    case 2: return u.format2.collect_coverage (glyphs);
+#ifndef HB_NO_BORING_EXPANSION
+    case 3: return u.format3.collect_coverage (glyphs);
+    case 4: return u.format4.collect_coverage (glyphs);
+#endif
+    default:return false;
+    }
+  }
+
+  template <typename IterableOut,
+	    hb_requires (hb_is_sink_of (IterableOut, hb_codepoint_t))>
+  void intersect_set (const hb_set_t &glyphs, IterableOut &intersect_glyphs) const
+  {
+    switch (u.format)
+    {
+    case 1: return u.format1.intersect_set (glyphs, intersect_glyphs);
+    case 2: return u.format2.intersect_set (glyphs, intersect_glyphs);
+#ifndef HB_NO_BORING_EXPANSION
+    case 3: return u.format3.intersect_set (glyphs, intersect_glyphs);
+    case 4: return u.format4.intersect_set (glyphs, intersect_glyphs);
+#endif
+    default:return ;
+    }
+  }
+
+  struct iter_t : hb_iter_with_fallback_t<iter_t, hb_codepoint_t>
+  {
+    static constexpr bool is_sorted_iterator = true;
+    iter_t (const Coverage &c_ = Null (Coverage))
+    {
+      memset (this, 0, sizeof (*this));
+      format = c_.u.format;
+      switch (format)
+      {
+      case 1: u.format1.init (c_.u.format1); return;
+      case 2: u.format2.init (c_.u.format2); return;
+#ifndef HB_NO_BORING_EXPANSION
+      case 3: u.format3.init (c_.u.format3); return;
+      case 4: u.format4.init (c_.u.format4); return;
+#endif
+      default:                               return;
+      }
+    }
+    bool __more__ () const
+    {
+      switch (format)
+      {
+      case 1: return u.format1.__more__ ();
+      case 2: return u.format2.__more__ ();
+#ifndef HB_NO_BORING_EXPANSION
+      case 3: return u.format3.__more__ ();
+      case 4: return u.format4.__more__ ();
+#endif
+      default:return false;
+      }
+    }
+    void __next__ ()
+    {
+      switch (format)
+      {
+      case 1: u.format1.__next__ (); break;
+      case 2: u.format2.__next__ (); break;
+#ifndef HB_NO_BORING_EXPANSION
+      case 3: u.format3.__next__ (); break;
+      case 4: u.format4.__next__ (); break;
+#endif
+      default:                   break;
+      }
+    }
+    typedef hb_codepoint_t __item_t__;
+    __item_t__ __item__ () const { return get_glyph (); }
+
+    hb_codepoint_t get_glyph () const
+    {
+      switch (format)
+      {
+      case 1: return u.format1.get_glyph ();
+      case 2: return u.format2.get_glyph ();
+#ifndef HB_NO_BORING_EXPANSION
+      case 3: return u.format3.get_glyph ();
+      case 4: return u.format4.get_glyph ();
+#endif
+      default:return 0;
+      }
+    }
+    bool operator != (const iter_t& o) const
+    {
+      if (unlikely (format != o.format)) return true;
+      switch (format)
+      {
+      case 1: return u.format1 != o.u.format1;
+      case 2: return u.format2 != o.u.format2;
+#ifndef HB_NO_BORING_EXPANSION
+      case 3: return u.format3 != o.u.format3;
+      case 4: return u.format4 != o.u.format4;
+#endif
+      default:return false;
+      }
+    }
+    iter_t __end__ () const
+    {
+      iter_t it = {};
+      it.format = format;
+      switch (format)
+      {
+      case 1: it.u.format1 = u.format1.__end__ (); break;
+      case 2: it.u.format2 = u.format2.__end__ (); break;
+#ifndef HB_NO_BORING_EXPANSION
+      case 3: it.u.format3 = u.format3.__end__ (); break;
+      case 4: it.u.format4 = u.format4.__end__ (); break;
+#endif
+      default: break;
+      }
+      return it;
+    }
+
+    private:
+    unsigned int format;
+    union {
+#ifndef HB_NO_BORING_EXPANSION
+    CoverageFormat2_4<MediumTypes>::iter_t      format4; /* Put this one first since it's larger; helps shut up compiler. */
+    CoverageFormat1_3<MediumTypes>::iter_t      format3;
+#endif
+    CoverageFormat2_4<SmallTypes>::iter_t       format2; /* Put this one first since it's larger; helps shut up compiler. */
+    CoverageFormat1_3<SmallTypes>::iter_t       format1;
+    } u;
+  };
+  iter_t iter () const { return iter_t (*this); }
+};
+
+template<typename Iterator>
+static inline void
+Coverage_serialize (hb_serialize_context_t *c,
+                    Iterator it)
+{ c->start_embed<Coverage> ()->serialize (c, it); }
+
+}
+}
+}
+
+#endif  // #ifndef OT_LAYOUT_COMMON_COVERAGE_HH
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/Common/CoverageFormat1.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/Common/CoverageFormat1.hh
new file mode 100644
index 0000000000000000000000000000000000000000..886babd2d1ce5f9ad99fce8b6a2c07d24e3f671f
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/Common/CoverageFormat1.hh
@@ -0,0 +1,126 @@
+/*
+ * Copyright © 2007,2008,2009  Red Hat, Inc.
+ * Copyright © 2010,2012  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.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod, Garret Rieger
+ */
+
+
+#ifndef OT_LAYOUT_COMMON_COVERAGEFORMAT1_HH
+#define OT_LAYOUT_COMMON_COVERAGEFORMAT1_HH
+
+namespace OT {
+namespace Layout {
+namespace Common {
+
+#define NOT_COVERED             ((unsigned int) -1)
+
+template <typename Types>
+struct CoverageFormat1_3
+{
+  friend struct Coverage;
+
+  protected:
+  HBUINT16      coverageFormat; /* Format identifier--format = 1 */
+  SortedArray16Of<typename Types::HBGlyphID>
+                glyphArray;     /* Array of GlyphIDs--in numerical order */
+  public:
+  DEFINE_SIZE_ARRAY (4, glyphArray);
+
+  private:
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (glyphArray.sanitize (c));
+  }
+
+  unsigned int get_coverage (hb_codepoint_t glyph_id) const
+  {
+    unsigned int i;
+    glyphArray.bfind (glyph_id, &i, HB_NOT_FOUND_STORE, NOT_COVERED);
+    return i;
+  }
+
+  unsigned get_population () const
+  {
+    return glyphArray.len;
+  }
+
+  template <typename Iterator,
+      hb_requires (hb_is_sorted_source_of (Iterator, hb_codepoint_t))>
+  bool serialize (hb_serialize_context_t *c, Iterator glyphs)
+  {
+    TRACE_SERIALIZE (this);
+    return_trace (glyphArray.serialize (c, glyphs));
+  }
+
+  bool intersects (const hb_set_t *glyphs) const
+  {
+    /* TODO Speed up, using hb_set_next() and bsearch()? */
+    for (const auto& g : glyphArray.as_array ())
+      if (glyphs->has (g))
+        return true;
+    return false;
+  }
+  bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const
+  { return glyphs->has (glyphArray[index]); }
+
+  template <typename IterableOut,
+	    hb_requires (hb_is_sink_of (IterableOut, hb_codepoint_t))>
+  void intersect_set (const hb_set_t &glyphs, IterableOut &intersect_glyphs) const
+  {
+    unsigned count = glyphArray.len;
+    for (unsigned i = 0; i < count; i++)
+      if (glyphs.has (glyphArray[i]))
+        intersect_glyphs << glyphArray[i];
+  }
+
+  template <typename set_t>
+  bool collect_coverage (set_t *glyphs) const
+  { return glyphs->add_sorted_array (glyphArray.as_array ()); }
+
+  public:
+  /* Older compilers need this to be public. */
+  struct iter_t
+  {
+    void init (const struct CoverageFormat1_3 &c_) { c = &c_; i = 0; }
+    bool __more__ () const { return i < c->glyphArray.len; }
+    void __next__ () { i++; }
+    hb_codepoint_t get_glyph () const { return c->glyphArray[i]; }
+    bool operator != (const iter_t& o) const
+    { return i != o.i; }
+    iter_t __end__ () const { iter_t it; it.init (*c); it.i = c->glyphArray.len; return it; }
+
+    private:
+    const struct CoverageFormat1_3 *c;
+    unsigned int i;
+  };
+  private:
+};
+
+}
+}
+}
+
+#endif  // #ifndef OT_LAYOUT_COMMON_COVERAGEFORMAT1_HH
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/Common/CoverageFormat2.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/Common/CoverageFormat2.hh
new file mode 100644
index 0000000000000000000000000000000000000000..4ddb2a73e4b82228c00d48f3e231fda7ca22ff2f
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/Common/CoverageFormat2.hh
@@ -0,0 +1,233 @@
+/*
+ * Copyright © 2007,2008,2009  Red Hat, Inc.
+ * Copyright © 2010,2012  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.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod, Garret Rieger
+ */
+
+#ifndef OT_LAYOUT_COMMON_COVERAGEFORMAT2_HH
+#define OT_LAYOUT_COMMON_COVERAGEFORMAT2_HH
+
+#include "RangeRecord.hh"
+
+namespace OT {
+namespace Layout {
+namespace Common {
+
+template <typename Types>
+struct CoverageFormat2_4
+{
+  friend struct Coverage;
+
+  protected:
+  HBUINT16      coverageFormat; /* Format identifier--format = 2 */
+  SortedArray16Of<RangeRecord<Types>>
+                rangeRecord;    /* Array of glyph ranges--ordered by
+                                 * Start GlyphID. rangeCount entries
+                                 * long */
+  public:
+  DEFINE_SIZE_ARRAY (4, rangeRecord);
+
+  private:
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (rangeRecord.sanitize (c));
+  }
+
+  unsigned int get_coverage (hb_codepoint_t glyph_id) const
+  {
+    const RangeRecord<Types> &range = rangeRecord.bsearch (glyph_id);
+    return likely (range.first <= range.last)
+         ? (unsigned int) range.value + (glyph_id - range.first)
+         : NOT_COVERED;
+  }
+
+  unsigned get_population () const
+  {
+    typename Types::large_int ret = 0;
+    for (const auto &r : rangeRecord)
+      ret += r.get_population ();
+    return ret > UINT_MAX ? UINT_MAX : (unsigned) ret;
+  }
+
+  template <typename Iterator,
+      hb_requires (hb_is_sorted_source_of (Iterator, hb_codepoint_t))>
+  bool serialize (hb_serialize_context_t *c, Iterator glyphs)
+  {
+    TRACE_SERIALIZE (this);
+    if (unlikely (!c->extend_min (this))) return_trace (false);
+
+    /* TODO(iter) Write more efficiently? */
+
+    unsigned num_ranges = 0;
+    hb_codepoint_t last = (hb_codepoint_t) -2;
+    for (auto g: glyphs)
+    {
+      if (last + 1 != g)
+        num_ranges++;
+      last = g;
+    }
+
+    if (unlikely (!rangeRecord.serialize (c, num_ranges))) return_trace (false);
+    if (!num_ranges) return_trace (true);
+
+    unsigned count = 0;
+    unsigned range = (unsigned) -1;
+    last = (hb_codepoint_t) -2;
+    for (auto g: glyphs)
+    {
+      if (last + 1 != g)
+      {
+        range++;
+        rangeRecord[range].first = g;
+        rangeRecord[range].value = count;
+      }
+      rangeRecord[range].last = g;
+      last = g;
+      count++;
+    }
+
+    return_trace (true);
+  }
+
+  bool intersects (const hb_set_t *glyphs) const
+  {
+    return hb_any (+ hb_iter (rangeRecord)
+                   | hb_map ([glyphs] (const RangeRecord<Types> &range) { return range.intersects (*glyphs); }));
+  }
+  bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const
+  {
+    auto cmp = [] (const void *pk, const void *pr) -> int
+    {
+      unsigned index = * (const unsigned *) pk;
+      const RangeRecord<Types> &range = * (const RangeRecord<Types> *) pr;
+      if (index < range.value) return -1;
+      if (index > (unsigned int) range.value + (range.last - range.first)) return +1;
+      return 0;
+    };
+
+    auto arr = rangeRecord.as_array ();
+    unsigned idx;
+    if (hb_bsearch_impl (&idx, index,
+                         arr.arrayZ, arr.length, sizeof (arr[0]),
+                         (int (*)(const void *_key, const void *_item)) cmp))
+      return arr.arrayZ[idx].intersects (*glyphs);
+    return false;
+  }
+
+  template <typename IterableOut,
+	    hb_requires (hb_is_sink_of (IterableOut, hb_codepoint_t))>
+  void intersect_set (const hb_set_t &glyphs, IterableOut &intersect_glyphs) const
+  {
+    for (const auto& range : rangeRecord)
+    {
+      hb_codepoint_t last = range.last;
+      for (hb_codepoint_t g = range.first - 1;
+	   glyphs.next (&g) && g <= last;)
+	intersect_glyphs << g;
+    }
+  }
+
+  template <typename set_t>
+  bool collect_coverage (set_t *glyphs) const
+  {
+    for (const auto& range: rangeRecord)
+      if (unlikely (!range.collect_coverage (glyphs)))
+        return false;
+    return true;
+  }
+
+  public:
+  /* Older compilers need this to be public. */
+  struct iter_t
+  {
+    void init (const CoverageFormat2_4 &c_)
+    {
+      c = &c_;
+      coverage = 0;
+      i = 0;
+      j = c->rangeRecord.len ? c->rangeRecord[0].first : 0;
+      if (unlikely (c->rangeRecord[0].first > c->rangeRecord[0].last))
+      {
+        /* Broken table. Skip. */
+        i = c->rangeRecord.len;
+        j = 0;
+      }
+    }
+    bool __more__ () const { return i < c->rangeRecord.len; }
+    void __next__ ()
+    {
+      if (j >= c->rangeRecord[i].last)
+      {
+        i++;
+        if (__more__ ())
+        {
+          unsigned int old = coverage;
+          j = c->rangeRecord[i].first;
+          coverage = c->rangeRecord[i].value;
+          if (unlikely (coverage != old + 1))
+          {
+            /* Broken table. Skip. Important to avoid DoS.
+             * Also, our callers depend on coverage being
+             * consecutive and monotonically increasing,
+             * ie. iota(). */
+           i = c->rangeRecord.len;
+           j = 0;
+           return;
+          }
+        }
+        else
+          j = 0;
+        return;
+      }
+      coverage++;
+      j++;
+    }
+    hb_codepoint_t get_glyph () const { return j; }
+    bool operator != (const iter_t& o) const
+    { return i != o.i || j != o.j; }
+    iter_t __end__ () const
+    {
+      iter_t it;
+      it.init (*c);
+      it.i = c->rangeRecord.len;
+      it.j = 0;
+      return it;
+    }
+
+    private:
+    const struct CoverageFormat2_4 *c;
+    unsigned int i, coverage;
+    hb_codepoint_t j;
+  };
+  private:
+};
+
+}
+}
+}
+
+#endif  // #ifndef OT_LAYOUT_COMMON_COVERAGEFORMAT2_HH
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/Common/RangeRecord.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/Common/RangeRecord.hh
new file mode 100644
index 0000000000000000000000000000000000000000..a62629fad343981cb5e4913505de7a5e5beb8971
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/Common/RangeRecord.hh
@@ -0,0 +1,85 @@
+/*
+ * Copyright © 2007,2008,2009  Red Hat, Inc.
+ * Copyright © 2010,2012  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.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod, Garret Rieger
+ */
+
+#ifndef OT_LAYOUT_COMMON_RANGERECORD_HH
+#define OT_LAYOUT_COMMON_RANGERECORD_HH
+
+namespace OT {
+namespace Layout {
+namespace Common {
+
+template <typename Types>
+struct RangeRecord
+{
+  typename Types::HBGlyphID     first;          /* First GlyphID in the range */
+  typename Types::HBGlyphID     last;           /* Last GlyphID in the range */
+  HBUINT16                      value;          /* Value */
+
+  DEFINE_SIZE_STATIC (2 + 2 * Types::size);
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  int cmp (hb_codepoint_t g) const
+  { return g < first ? -1 : g <= last ? 0 : +1; }
+
+  unsigned get_population () const
+  {
+    if (unlikely (last < first)) return 0;
+    return (last - first + 1);
+  }
+
+  bool intersects (const hb_set_t &glyphs) const
+  { return glyphs.intersects (first, last); }
+
+  template <typename set_t>
+  bool collect_coverage (set_t *glyphs) const
+  { return glyphs->add_range (first, last); }
+};
+
+}
+}
+}
+
+// TODO(garretrieger): This was previously implemented using
+//    DECLARE_NULL_NAMESPACE_BYTES_TEMPLATE1 (OT, RangeRecord, 9);
+//    but that only works when there is only a single namespace level.
+//    The macro should probably be fixed so it can work in this situation.
+extern HB_INTERNAL const unsigned char _hb_Null_OT_RangeRecord[9];
+template <typename Spec>
+struct Null<OT::Layout::Common::RangeRecord<Spec>> {
+  static OT::Layout::Common::RangeRecord<Spec> const & get_null () {
+    return *reinterpret_cast<const OT::Layout::Common::RangeRecord<Spec> *> (_hb_Null_OT_RangeRecord);
+  }
+};
+
+
+#endif  // #ifndef OT_LAYOUT_COMMON_RANGERECORD_HH
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/GPOS.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/GPOS.hh
new file mode 100644
index 0000000000000000000000000000000000000000..72829377a6974f6ba2b9d73d499b518fd168eada
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/GPOS.hh
@@ -0,0 +1,171 @@
+#ifndef OT_LAYOUT_GPOS_GPOS_HH
+#define OT_LAYOUT_GPOS_GPOS_HH
+
+#include "../../../hb-ot-layout-common.hh"
+#include "../../../hb-ot-layout-gsubgpos.hh"
+#include "Common.hh"
+#include "PosLookup.hh"
+
+namespace OT {
+
+using Layout::GPOS_impl::PosLookup;
+
+namespace Layout {
+
+static void
+propagate_attachment_offsets (hb_glyph_position_t *pos,
+                              unsigned int len,
+                              unsigned int i,
+                              hb_direction_t direction,
+                              unsigned nesting_level = HB_MAX_NESTING_LEVEL);
+
+/*
+ * GPOS -- Glyph Positioning
+ * https://docs.microsoft.com/en-us/typography/opentype/spec/gpos
+ */
+
+struct GPOS : GSUBGPOS
+{
+  static constexpr hb_tag_t tableTag = HB_OT_TAG_GPOS;
+
+  using Lookup = PosLookup;
+
+  const PosLookup& get_lookup (unsigned int i) const
+  { return static_cast<const PosLookup &> (GSUBGPOS::get_lookup (i)); }
+
+  static inline void position_start (hb_font_t *font, hb_buffer_t *buffer);
+  static inline void position_finish_advances (hb_font_t *font, hb_buffer_t *buffer);
+  static inline void position_finish_offsets (hb_font_t *font, hb_buffer_t *buffer);
+
+  bool subset (hb_subset_context_t *c) const
+  {
+    hb_subset_layout_context_t l (c, tableTag, c->plan->gpos_lookups, c->plan->gpos_langsys, c->plan->gpos_features);
+    return GSUBGPOS::subset<PosLookup> (&l);
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (GSUBGPOS::sanitize<PosLookup> (c));
+  }
+
+  HB_INTERNAL bool is_blocklisted (hb_blob_t *blob,
+                                   hb_face_t *face) const;
+
+  void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
+  {
+    for (unsigned i = 0; i < GSUBGPOS::get_lookup_count (); i++)
+    {
+      if (!c->gpos_lookups->has (i)) continue;
+      const PosLookup &l = get_lookup (i);
+      l.dispatch (c);
+    }
+  }
+
+  void closure_lookups (hb_face_t      *face,
+                        const hb_set_t *glyphs,
+                        hb_set_t       *lookup_indexes /* IN/OUT */) const
+  { GSUBGPOS::closure_lookups<PosLookup> (face, glyphs, lookup_indexes); }
+
+  typedef GSUBGPOS::accelerator_t<GPOS> accelerator_t;
+};
+
+
+static void
+propagate_attachment_offsets (hb_glyph_position_t *pos,
+                              unsigned int len,
+                              unsigned int i,
+                              hb_direction_t direction,
+                              unsigned nesting_level)
+{
+  /* Adjusts offsets of attached glyphs (both cursive and mark) to accumulate
+   * offset of glyph they are attached to. */
+  int chain = pos[i].attach_chain(), type = pos[i].attach_type();
+  if (likely (!chain))
+    return;
+
+  pos[i].attach_chain() = 0;
+
+  unsigned int j = (int) i + chain;
+
+  if (unlikely (j >= len))
+    return;
+
+  if (unlikely (!nesting_level))
+    return;
+
+  propagate_attachment_offsets (pos, len, j, direction, nesting_level - 1);
+
+  assert (!!(type & GPOS_impl::ATTACH_TYPE_MARK) ^ !!(type & GPOS_impl::ATTACH_TYPE_CURSIVE));
+
+  if (type & GPOS_impl::ATTACH_TYPE_CURSIVE)
+  {
+    if (HB_DIRECTION_IS_HORIZONTAL (direction))
+      pos[i].y_offset += pos[j].y_offset;
+    else
+      pos[i].x_offset += pos[j].x_offset;
+  }
+  else /*if (type & GPOS_impl::ATTACH_TYPE_MARK)*/
+  {
+    pos[i].x_offset += pos[j].x_offset;
+    pos[i].y_offset += pos[j].y_offset;
+
+    assert (j < i);
+    if (HB_DIRECTION_IS_FORWARD (direction))
+      for (unsigned int k = j; k < i; k++) {
+        pos[i].x_offset -= pos[k].x_advance;
+        pos[i].y_offset -= pos[k].y_advance;
+      }
+    else
+      for (unsigned int k = j + 1; k < i + 1; k++) {
+        pos[i].x_offset += pos[k].x_advance;
+        pos[i].y_offset += pos[k].y_advance;
+      }
+  }
+}
+
+void
+GPOS::position_start (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer)
+{
+  unsigned int count = buffer->len;
+  for (unsigned int i = 0; i < count; i++)
+    buffer->pos[i].attach_chain() = buffer->pos[i].attach_type() = 0;
+}
+
+void
+GPOS::position_finish_advances (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer HB_UNUSED)
+{
+  //_hb_buffer_assert_gsubgpos_vars (buffer);
+}
+
+void
+GPOS::position_finish_offsets (hb_font_t *font, hb_buffer_t *buffer)
+{
+  _hb_buffer_assert_gsubgpos_vars (buffer);
+
+  unsigned int len;
+  hb_glyph_position_t *pos = hb_buffer_get_glyph_positions (buffer, &len);
+  hb_direction_t direction = buffer->props.direction;
+
+  /* Handle attachments */
+  if (buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT)
+    for (unsigned i = 0; i < len; i++)
+      propagate_attachment_offsets (pos, len, i, direction);
+
+  if (unlikely (font->slant))
+  {
+    for (unsigned i = 0; i < len; i++)
+      if (unlikely (pos[i].y_offset))
+        pos[i].x_offset += _hb_roundf (font->slant_xy * pos[i].y_offset);
+  }
+}
+
+}
+
+struct GPOS_accelerator_t : Layout::GPOS::accelerator_t {
+  GPOS_accelerator_t (hb_face_t *face) : Layout::GPOS::accelerator_t (face) {}
+};
+
+}
+
+#endif  /* OT_LAYOUT_GPOS_GPOS_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/LigatureArray.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/LigatureArray.hh
new file mode 100644
index 0000000000000000000000000000000000000000..a2d807cc3206c0f0af097f176f2b69ac2219bbb3
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/LigatureArray.hh
@@ -0,0 +1,56 @@
+#ifndef OT_LAYOUT_GPOS_LIGATUREARRAY_HH
+#define OT_LAYOUT_GPOS_LIGATUREARRAY_HH
+
+namespace OT {
+namespace Layout {
+namespace GPOS_impl {
+
+
+typedef AnchorMatrix LigatureAttach;    /* component-major--
+                                         * in order of writing direction--,
+                                         * mark-minor--
+                                         * ordered by class--zero-based. */
+
+/* Array of LigatureAttach tables ordered by LigatureCoverage Index */
+struct LigatureArray : List16OfOffset16To<LigatureAttach>
+{
+  template <typename Iterator,
+            hb_requires (hb_is_iterator (Iterator))>
+  bool subset (hb_subset_context_t *c,
+               Iterator             coverage,
+               unsigned             class_count,
+               const hb_map_t      *klass_mapping) const
+  {
+    TRACE_SUBSET (this);
+    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
+
+    auto *out = c->serializer->start_embed (this);
+    if (unlikely (!c->serializer->extend_min (out)))  return_trace (false);
+
+    for (const auto _ : + hb_zip (coverage, *this)
+                  | hb_filter (glyphset, hb_first))
+    {
+      auto *matrix = out->serialize_append (c->serializer);
+      if (unlikely (!matrix)) return_trace (false);
+
+      const LigatureAttach& src = (this + _.second);
+      auto indexes =
+          + hb_range (src.rows * class_count)
+          | hb_filter ([=] (unsigned index) { return klass_mapping->has (index % class_count); })
+          ;
+      matrix->serialize_subset (c,
+                                _.second,
+                                this,
+                                src.rows,
+                                indexes);
+    }
+    return_trace (this->len);
+  }
+};
+
+
+}
+}
+}
+
+#endif /* OT_LAYOUT_GPOS_LIGATUREARRAY_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/PairSet.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/PairSet.hh
new file mode 100644
index 0000000000000000000000000000000000000000..58ba4de6c19c2b5fa195c1f417b722e655923de2
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/PairSet.hh
@@ -0,0 +1,173 @@
+#ifndef OT_LAYOUT_GPOS_PAIRSET_HH
+#define OT_LAYOUT_GPOS_PAIRSET_HH
+
+#include "PairValueRecord.hh"
+
+namespace OT {
+namespace Layout {
+namespace GPOS_impl {
+
+
+template <typename Types>
+struct PairSet
+{
+  template <typename Types2>
+  friend struct PairPosFormat1_3;
+
+  using PairValueRecord = GPOS_impl::PairValueRecord<Types>;
+
+  protected:
+  HBUINT16              len;    /* Number of PairValueRecords */
+  PairValueRecord       firstPairValueRecord;
+                                /* Array of PairValueRecords--ordered
+                                 * by GlyphID of the second glyph */
+  public:
+  DEFINE_SIZE_MIN (2);
+
+  struct sanitize_closure_t
+  {
+    const ValueFormat *valueFormats;
+    unsigned int len1; /* valueFormats[0].get_len() */
+    unsigned int stride; /* 1 + len1 + len2 */
+  };
+
+  bool sanitize (hb_sanitize_context_t *c, const sanitize_closure_t *closure) const
+  {
+    TRACE_SANITIZE (this);
+    if (!(c->check_struct (this)
+       && c->check_range (&firstPairValueRecord,
+                          len,
+                          HBUINT16::static_size,
+                          closure->stride))) return_trace (false);
+
+    unsigned int count = len;
+    const PairValueRecord *record = &firstPairValueRecord;
+    return_trace (closure->valueFormats[0].sanitize_values_stride_unsafe (c, this, &record->values[0], count, closure->stride) &&
+                  closure->valueFormats[1].sanitize_values_stride_unsafe (c, this, &record->values[closure->len1], count, closure->stride));
+  }
+
+  bool intersects (const hb_set_t *glyphs,
+                   const ValueFormat *valueFormats) const
+  {
+    unsigned int len1 = valueFormats[0].get_len ();
+    unsigned int len2 = valueFormats[1].get_len ();
+    unsigned int record_size = HBUINT16::static_size * (1 + len1 + len2);
+
+    const PairValueRecord *record = &firstPairValueRecord;
+    unsigned int count = len;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      if (glyphs->has (record->secondGlyph))
+        return true;
+      record = &StructAtOffset<const PairValueRecord> (record, record_size);
+    }
+    return false;
+  }
+
+  void collect_glyphs (hb_collect_glyphs_context_t *c,
+                       const ValueFormat *valueFormats) const
+  {
+    unsigned int len1 = valueFormats[0].get_len ();
+    unsigned int len2 = valueFormats[1].get_len ();
+    unsigned int record_size = HBUINT16::static_size * (1 + len1 + len2);
+
+    const PairValueRecord *record = &firstPairValueRecord;
+    c->input->add_array (&record->secondGlyph, len, record_size);
+  }
+
+  void collect_variation_indices (hb_collect_variation_indices_context_t *c,
+                                  const ValueFormat *valueFormats) const
+  {
+    unsigned len1 = valueFormats[0].get_len ();
+    unsigned len2 = valueFormats[1].get_len ();
+    unsigned record_size = HBUINT16::static_size * (1 + len1 + len2);
+
+    const PairValueRecord *record = &firstPairValueRecord;
+    unsigned count = len;
+    for (unsigned i = 0; i < count; i++)
+    {
+      if (c->glyph_set->has (record->secondGlyph))
+      { record->collect_variation_indices (c, valueFormats, this); }
+
+      record = &StructAtOffset<const PairValueRecord> (record, record_size);
+    }
+  }
+
+  bool apply (hb_ot_apply_context_t *c,
+              const ValueFormat *valueFormats,
+              unsigned int pos) const
+  {
+    TRACE_APPLY (this);
+    hb_buffer_t *buffer = c->buffer;
+    unsigned int len1 = valueFormats[0].get_len ();
+    unsigned int len2 = valueFormats[1].get_len ();
+    unsigned int record_size = HBUINT16::static_size * (1 + len1 + len2);
+
+    const PairValueRecord *record = hb_bsearch (buffer->info[pos].codepoint,
+                                                &firstPairValueRecord,
+                                                len,
+                                                record_size);
+    if (record)
+    {
+      bool applied_first = valueFormats[0].apply_value (c, this, &record->values[0], buffer->cur_pos());
+      bool applied_second = valueFormats[1].apply_value (c, this, &record->values[len1], buffer->pos[pos]);
+      if (applied_first || applied_second)
+        buffer->unsafe_to_break (buffer->idx, pos + 1);
+      if (len2)
+        pos++;
+      buffer->idx = pos;
+      return_trace (true);
+    }
+    buffer->unsafe_to_concat (buffer->idx, pos + 1);
+    return_trace (false);
+  }
+
+  bool subset (hb_subset_context_t *c,
+               const ValueFormat valueFormats[2],
+               const ValueFormat newFormats[2]) const
+  {
+    TRACE_SUBSET (this);
+    auto snap = c->serializer->snapshot ();
+
+    auto *out = c->serializer->start_embed (*this);
+    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+    out->len = 0;
+
+    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
+    const hb_map_t &glyph_map = *c->plan->glyph_map;
+
+    unsigned len1 = valueFormats[0].get_len ();
+    unsigned len2 = valueFormats[1].get_len ();
+    unsigned record_size = HBUINT16::static_size + Value::static_size * (len1 + len2);
+
+    typename PairValueRecord::context_t context =
+    {
+      this,
+      valueFormats,
+      newFormats,
+      len1,
+      &glyph_map,
+      c->plan->layout_variation_idx_map
+    };
+
+    const PairValueRecord *record = &firstPairValueRecord;
+    unsigned count = len, num = 0;
+    for (unsigned i = 0; i < count; i++)
+    {
+      if (glyphset.has (record->secondGlyph)
+         && record->subset (c, &context)) num++;
+      record = &StructAtOffset<const PairValueRecord> (record, record_size);
+    }
+
+    out->len = num;
+    if (!num) c->serializer->revert (snap);
+    return_trace (num);
+  }
+};
+
+
+}
+}
+}
+
+#endif  // OT_LAYOUT_GPOS_PAIRSET_HH
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/PairValueRecord.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/PairValueRecord.hh
new file mode 100644
index 0000000000000000000000000000000000000000..55de5abebf68e6eba88d14939931028861e5ee3b
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/GPOS/PairValueRecord.hh
@@ -0,0 +1,97 @@
+#ifndef OT_LAYOUT_GPOS_PAIRVALUERECORD_HH
+#define OT_LAYOUT_GPOS_PAIRVALUERECORD_HH
+
+namespace OT {
+namespace Layout {
+namespace GPOS_impl {
+
+
+template <typename Types>
+struct PairValueRecord
+{
+  template <typename Types2>
+  friend struct PairSet;
+
+  protected:
+  typename Types::HBGlyphID
+	        secondGlyph;            /* GlyphID of second glyph in the
+                                         * pair--first glyph is listed in the
+                                         * Coverage table */
+  ValueRecord   values;                 /* Positioning data for the first glyph
+                                         * followed by for second glyph */
+  public:
+  DEFINE_SIZE_ARRAY (Types::size, values);
+
+  int cmp (hb_codepoint_t k) const
+  { return secondGlyph.cmp (k); }
+
+  struct context_t
+  {
+    const void          *base;
+    const ValueFormat   *valueFormats;
+    const ValueFormat   *newFormats;
+    unsigned            len1; /* valueFormats[0].get_len() */
+    const hb_map_t      *glyph_map;
+    const hb_map_t      *layout_variation_idx_map;
+  };
+
+  bool subset (hb_subset_context_t *c,
+               context_t *closure) const
+  {
+    TRACE_SERIALIZE (this);
+    auto *s = c->serializer;
+    auto *out = s->start_embed (*this);
+    if (unlikely (!s->extend_min (out))) return_trace (false);
+
+    out->secondGlyph = (*closure->glyph_map)[secondGlyph];
+
+    closure->valueFormats[0].copy_values (s,
+                                          closure->newFormats[0],
+                                          closure->base, &values[0],
+                                          closure->layout_variation_idx_map);
+    closure->valueFormats[1].copy_values (s,
+                                          closure->newFormats[1],
+                                          closure->base,
+                                          &values[closure->len1],
+                                          closure->layout_variation_idx_map);
+
+    return_trace (true);
+  }
+
+  void collect_variation_indices (hb_collect_variation_indices_context_t *c,
+                                  const ValueFormat *valueFormats,
+                                  const void *base) const
+  {
+    unsigned record1_len = valueFormats[0].get_len ();
+    unsigned record2_len = valueFormats[1].get_len ();
+    const hb_array_t<const Value> values_array = values.as_array (record1_len + record2_len);
+
+    if (valueFormats[0].has_device ())
+      valueFormats[0].collect_variation_indices (c, base, values_array.sub_array (0, record1_len));
+
+    if (valueFormats[1].has_device ())
+      valueFormats[1].collect_variation_indices (c, base, values_array.sub_array (record1_len, record2_len));
+  }
+
+  bool intersects (const hb_set_t& glyphset) const
+  {
+    return glyphset.has(secondGlyph);
+  }
+
+  const Value* get_values_1 () const
+  {
+    return &values[0];
+  }
+
+  const Value* get_values_2 (ValueFormat format1) const
+  {
+    return &values[format1.get_len ()];
+  }
+};
+
+
+}
+}
+}
+
+#endif  // OT_LAYOUT_GPOS_PAIRVALUERECORD_HH
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/types.hh b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/types.hh
new file mode 100644
index 0000000000000000000000000000000000000000..6a43403e94b2da6b603bcf46f461fbc5065ce6fe
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/OT/Layout/types.hh
@@ -0,0 +1,66 @@
+/*
+ * Copyright © 2007,2008,2009  Red Hat, Inc.
+ * Copyright © 2010,2012  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.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod, Garret Rieger
+ */
+
+#ifndef OT_LAYOUT_TYPES_HH
+#define OT_LAYOUT_TYPES_HH
+
+namespace OT {
+namespace Layout {
+
+struct SmallTypes {
+  static constexpr unsigned size = 2;
+  using large_int = uint32_t;
+  using HBUINT = HBUINT16;
+  using HBGlyphID = HBGlyphID16;
+  using Offset = Offset16;
+  template <typename Type, bool has_null=true>
+  using OffsetTo = OT::Offset16To<Type, has_null>;
+  template <typename Type>
+  using ArrayOf = OT::Array16Of<Type>;
+  template <typename Type>
+  using SortedArrayOf = OT::SortedArray16Of<Type>;
+};
+
+struct MediumTypes {
+  static constexpr unsigned size = 3;
+  using large_int = uint64_t;
+  using HBUINT = HBUINT24;
+  using HBGlyphID = HBGlyphID24;
+  using Offset = Offset24;
+  template <typename Type, bool has_null=true>
+  using OffsetTo = OT::Offset24To<Type, has_null>;
+  template <typename Type>
+  using ArrayOf = OT::Array24Of<Type>;
+  template <typename Type>
+  using SortedArrayOf = OT::SortedArray24Of<Type>;
+};
+
+}
+}
+
+#endif  /* OT_LAYOUT_TYPES_HH */
diff --git a/source/libs/harfbuzz/harfbuzz-src/src/harfbuzz-subset.cc b/source/libs/harfbuzz/harfbuzz-src/src/harfbuzz-subset.cc
new file mode 100644
index 0000000000000000000000000000000000000000..39e7f14fafc9c125ae39eae667f47b11cd334157
--- /dev/null
+++ b/source/libs/harfbuzz/harfbuzz-src/src/harfbuzz-subset.cc
@@ -0,0 +1,56 @@
+#include "hb-aat-layout.cc"
+#include "hb-aat-map.cc"
+#include "hb-blob.cc"
+#include "hb-buffer-serialize.cc"
+#include "hb-buffer-verify.cc"
+#include "hb-buffer.cc"
+#include "hb-common.cc"
+#include "hb-draw.cc"
+#include "hb-face.cc"
+#include "hb-fallback-shape.cc"
+#include "hb-font.cc"
+#include "hb-map.cc"
+#include "hb-number.cc"
+#include "hb-ot-cff1-table.cc"
+#include "hb-ot-cff2-table.cc"
+#include "hb-ot-color.cc"
+#include "hb-ot-face.cc"
+#include "hb-ot-font.cc"
+#include "hb-ot-layout.cc"
+#include "hb-ot-map.cc"
+#include "hb-ot-math.cc"
+#include "hb-ot-meta.cc"
+#include "hb-ot-metrics.cc"
+#include "hb-ot-name.cc"
+#include "hb-ot-shape-fallback.cc"
+#include "hb-ot-shape-normalize.cc"
+#include "hb-ot-shape.cc"
+#include "hb-ot-shaper-arabic.cc"
+#include "hb-ot-shaper-default.cc"
+#include "hb-ot-shaper-hangul.cc"
+#include "hb-ot-shaper-hebrew.cc"
+#include "hb-ot-shaper-indic-table.cc"
+#include "hb-ot-shaper-indic.cc"
+#include "hb-ot-shaper-khmer.cc"
+#include "hb-ot-shaper-myanmar.cc"
+#include "hb-ot-shaper-syllabic.cc"
+#include "hb-ot-shaper-thai.cc"
+#include "hb-ot-shaper-use.cc"
+#include "hb-ot-shaper-vowel-constraints.cc"
+#include "hb-ot-tag.cc"
+#include "hb-ot-var.cc"
+#include "hb-set.cc"
+#include "hb-shape-plan.cc"
+#include "hb-shape.cc"
+#include "hb-shaper.cc"
+#include "hb-static.cc"
+#include "hb-style.cc"
+#include "hb-subset-cff-common.cc"
+#include "hb-subset-cff1.cc"
+#include "hb-subset-cff2.cc"
+#include "hb-subset-input.cc"
+#include "hb-subset-plan.cc"
+#include "hb-subset-repacker.cc"
+#include "hb-subset.cc"
+#include "hb-ucd.cc"
+#include "hb-unicode.cc"
diff --git a/source/texk/web2c/luatexdir/ChangeLog b/source/texk/web2c/luatexdir/ChangeLog
index 70497ad9bfef52c96a96f79076e423eb95ef2e22..f98db659a804ef68c24a85611a53c1c02384d2fb 100644
--- a/source/texk/web2c/luatexdir/ChangeLog
+++ b/source/texk/web2c/luatexdir/ChangeLog
@@ -1,3 +1,6 @@
+2022-07-30  Luigi Scarso <luigi.scarso@gmail.com>
+    * Take exception pre/port disc font from wordstart (H.Hagen)
+
 2022-05-23  Luigi Scarso <luigi.scarso@gmail.com>
     * Fixed mp_begin_iteration (H.Hagen)
 
diff --git a/source/texk/web2c/luatexdir/lang/texlang.c b/source/texk/web2c/luatexdir/lang/texlang.c
index 67ef25ca1d1b5687c5402bdcefd0e0f40d09e715..f9e53bbbabf4ba4c6ae8e377419b7760f9f00ebf 100644
--- a/source/texk/web2c/luatexdir/lang/texlang.c
+++ b/source/texk/web2c/luatexdir/lang/texlang.c
@@ -358,7 +358,9 @@ static halfword insert_discretionary(halfword t, halfword pre, halfword post, ha
         f = get_cur_font();
     }
     for (g = pre; g != null; g = vlink(g)) {
-        font(g) = f;
+        if (! font(g)) {
+            font(g) = f;
+        }
         if (attr != null) {
             delete_attribute_ref(node_attr(g));
             node_attr(g) = attr;
@@ -366,7 +368,9 @@ static halfword insert_discretionary(halfword t, halfword pre, halfword post, ha
         }
     }
     for (g = post; g != null; g = vlink(g)) {
-        font(g) = f;
+        if (! font(g)) {
+            font(g) = f;
+        }
         if (attr != null) {
             delete_attribute_ref(node_attr(g));
             node_attr(g) = attr;
@@ -531,9 +535,14 @@ char *exception_strings(struct tex_language *lang)
     The sequence from |wordstart| to |r| can contain only normal characters it
     could be faster to modify a halfword pointer and return an integer
 
+    We now take the font from the wordstart (as in \LUAMETATEX) but leave the
+    rest as it is, because we don't want to break compatibility (end June 2022).
+    We make a copy now of the parent and hope for the best. Backporting would be
+    too intrusive so this has to do. It went unnoticed for ages anyway.
+
 */
 
-static halfword find_exception_part(unsigned int *j, unsigned int *uword, int len)
+static halfword find_exception_part(unsigned int *j, unsigned int *uword, int len, halfword parent)
 {
     halfword g = null, gg = null;
     register unsigned i = *j;
@@ -541,13 +550,16 @@ static halfword find_exception_part(unsigned int *j, unsigned int *uword, int le
     i++;
     while (i < (unsigned) len && uword[i + 1] != '}') {
         if (g == null) {
-            gg = new_char(0, (int) uword[i + 1]);
+         /* gg = new_char(font(parent), (int) uword[i + 1]); */
+            gg = copy_node(parent);
             g = gg;
         } else {
-            halfword s = new_char(0, (int) uword[i + 1]);
+         /* halfword s = new_char(font(parent), (int) uword[i + 1]); */
+            halfword s = copy_node(parent);
             couple_nodes(g, s);
-            g = vlink(g);
+            g = s;
         }
+        character(g) = (int) uword[i + 1];
         i++;
     }
     *j = ++i;
@@ -614,12 +626,12 @@ static void do_exception(halfword wordstart, halfword r, char *replacement)
             halfword gg, hh, replace = null;
             int repl;
             /*tex |pre| */
-            gg = find_exception_part(&i, uword, (int) len);
+            gg = find_exception_part(&i, uword, (int) len, wordstart);
             if (i == len || uword[i + 1] != '{') {
                 tex_error("broken pattern 1", PAT_ERROR);
             }
             /*tex |post| */
-            hh = find_exception_part(&i, uword, (int) len);
+            hh = find_exception_part(&i, uword, (int) len, wordstart);
             if (i == len || uword[i + 1] != '{') {
                 tex_error("broken pattern 2", PAT_ERROR);
             }
diff --git a/source/texk/web2c/luatexdir/luatex_svnversion.h b/source/texk/web2c/luatexdir/luatex_svnversion.h
index a7074e3525d6a55379e104c67907bc4f3519bd0b..50224b636a6f9a70bc34bb2339ae20d21fe8adc8 100644
--- a/source/texk/web2c/luatexdir/luatex_svnversion.h
+++ b/source/texk/web2c/luatexdir/luatex_svnversion.h
@@ -1,4 +1,4 @@
 #ifndef luatex_svn_revision_h
 #define luatex_svn_revision_h
-#define luatex_svn_revision 7530
+#define luatex_svn_revision 7531
 #endif