summaryrefslogtreecommitdiffstats
path: root/gfx/harfbuzz/src/OT/Layout/GSUB
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /gfx/harfbuzz/src/OT/Layout/GSUB
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gfx/harfbuzz/src/OT/Layout/GSUB')
-rw-r--r--gfx/harfbuzz/src/OT/Layout/GSUB/AlternateSet.hh126
-rw-r--r--gfx/harfbuzz/src/OT/Layout/GSUB/AlternateSubst.hh62
-rw-r--r--gfx/harfbuzz/src/OT/Layout/GSUB/AlternateSubstFormat1.hh128
-rw-r--r--gfx/harfbuzz/src/OT/Layout/GSUB/ChainContextSubst.hh18
-rw-r--r--gfx/harfbuzz/src/OT/Layout/GSUB/Common.hh21
-rw-r--r--gfx/harfbuzz/src/OT/Layout/GSUB/ContextSubst.hh18
-rw-r--r--gfx/harfbuzz/src/OT/Layout/GSUB/ExtensionSubst.hh22
-rw-r--r--gfx/harfbuzz/src/OT/Layout/GSUB/GSUB.hh61
-rw-r--r--gfx/harfbuzz/src/OT/Layout/GSUB/Ligature.hh190
-rw-r--r--gfx/harfbuzz/src/OT/Layout/GSUB/LigatureSet.hh189
-rw-r--r--gfx/harfbuzz/src/OT/Layout/GSUB/LigatureSubst.hh71
-rw-r--r--gfx/harfbuzz/src/OT/Layout/GSUB/LigatureSubstFormat1.hh166
-rw-r--r--gfx/harfbuzz/src/OT/Layout/GSUB/MultipleSubst.hh62
-rw-r--r--gfx/harfbuzz/src/OT/Layout/GSUB/MultipleSubstFormat1.hh130
-rw-r--r--gfx/harfbuzz/src/OT/Layout/GSUB/ReverseChainSingleSubst.hh36
-rw-r--r--gfx/harfbuzz/src/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh244
-rw-r--r--gfx/harfbuzz/src/OT/Layout/GSUB/Sequence.hh165
-rw-r--r--gfx/harfbuzz/src/OT/Layout/GSUB/SingleSubst.hh103
-rw-r--r--gfx/harfbuzz/src/OT/Layout/GSUB/SingleSubstFormat1.hh204
-rw-r--r--gfx/harfbuzz/src/OT/Layout/GSUB/SingleSubstFormat2.hh176
-rw-r--r--gfx/harfbuzz/src/OT/Layout/GSUB/SubstLookup.hh220
-rw-r--r--gfx/harfbuzz/src/OT/Layout/GSUB/SubstLookupSubTable.hh77
22 files changed, 2489 insertions, 0 deletions
diff --git a/gfx/harfbuzz/src/OT/Layout/GSUB/AlternateSet.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/AlternateSet.hh
new file mode 100644
index 0000000000..b4466119be
--- /dev/null
+++ b/gfx/harfbuzz/src/OT/Layout/GSUB/AlternateSet.hh
@@ -0,0 +1,126 @@
+#ifndef OT_LAYOUT_GSUB_ALTERNATESET_HH
+#define OT_LAYOUT_GSUB_ALTERNATESET_HH
+
+#include "Common.hh"
+
+namespace OT {
+namespace Layout {
+namespace GSUB_impl {
+
+template <typename Types>
+struct AlternateSet
+{
+ protected:
+ Array16Of<typename Types::HBGlyphID>
+ alternates; /* Array of alternate GlyphIDs--in
+ * arbitrary order */
+ public:
+ DEFINE_SIZE_ARRAY (2, alternates);
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (alternates.sanitize (c));
+ }
+
+ bool intersects (const hb_set_t *glyphs) const
+ { return hb_any (alternates, glyphs); }
+
+ void closure (hb_closure_context_t *c) const
+ { c->output->add_array (alternates.arrayZ, alternates.len); }
+
+ void collect_glyphs (hb_collect_glyphs_context_t *c) const
+ { c->output->add_array (alternates.arrayZ, alternates.len); }
+
+ bool apply (hb_ot_apply_context_t *c) const
+ {
+ TRACE_APPLY (this);
+ unsigned int count = alternates.len;
+
+ if (unlikely (!count)) return_trace (false);
+
+ hb_mask_t glyph_mask = c->buffer->cur().mask;
+ hb_mask_t lookup_mask = c->lookup_mask;
+
+ /* Note: This breaks badly if two features enabled this lookup together. */
+ unsigned int shift = hb_ctz (lookup_mask);
+ unsigned int alt_index = ((lookup_mask & glyph_mask) >> shift);
+
+ /* If alt_index is MAX_VALUE, randomize feature if it is the rand feature. */
+ if (alt_index == HB_OT_MAP_MAX_VALUE && c->random)
+ {
+ /* Maybe we can do better than unsafe-to-break all; but since we are
+ * changing random state, it would be hard to track that. Good 'nough. */
+ c->buffer->unsafe_to_break (0, c->buffer->len);
+ alt_index = c->random_number () % count + 1;
+ }
+
+ if (unlikely (alt_index > count || alt_index == 0)) return_trace (false);
+
+ if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+ {
+ c->buffer->sync_so_far ();
+ c->buffer->message (c->font,
+ "replacing glyph at %u (alternate substitution)",
+ c->buffer->idx);
+ }
+
+ c->replace_glyph (alternates[alt_index - 1]);
+
+ if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+ {
+ c->buffer->message (c->font,
+ "replaced glyph at %u (alternate substitution)",
+ c->buffer->idx - 1u);
+ }
+
+ return_trace (true);
+ }
+
+ unsigned
+ get_alternates (unsigned start_offset,
+ unsigned *alternate_count /* IN/OUT. May be NULL. */,
+ hb_codepoint_t *alternate_glyphs /* OUT. May be NULL. */) const
+ {
+ if (alternates.len && alternate_count)
+ {
+ + alternates.as_array ().sub_array (start_offset, alternate_count)
+ | hb_sink (hb_array (alternate_glyphs, *alternate_count))
+ ;
+ }
+ return alternates.len;
+ }
+
+ template <typename Iterator,
+ hb_requires (hb_is_source_of (Iterator, hb_codepoint_t))>
+ bool serialize (hb_serialize_context_t *c,
+ Iterator alts)
+ {
+ TRACE_SERIALIZE (this);
+ return_trace (alternates.serialize (c, alts));
+ }
+
+ bool subset (hb_subset_context_t *c) const
+ {
+ TRACE_SUBSET (this);
+ const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
+ const hb_map_t &glyph_map = *c->plan->glyph_map;
+
+ auto it =
+ + hb_iter (alternates)
+ | hb_filter (glyphset)
+ | hb_map (glyph_map)
+ ;
+
+ auto *out = c->serializer->start_embed (*this);
+ return_trace (out->serialize (c->serializer, it) &&
+ out->alternates);
+ }
+};
+
+}
+}
+}
+
+
+#endif /* OT_LAYOUT_GSUB_ALTERNATESET_HH */
diff --git a/gfx/harfbuzz/src/OT/Layout/GSUB/AlternateSubst.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/AlternateSubst.hh
new file mode 100644
index 0000000000..04a052a783
--- /dev/null
+++ b/gfx/harfbuzz/src/OT/Layout/GSUB/AlternateSubst.hh
@@ -0,0 +1,62 @@
+#ifndef OT_LAYOUT_GSUB_ALTERNATESUBST_HH
+#define OT_LAYOUT_GSUB_ALTERNATESUBST_HH
+
+#include "AlternateSubstFormat1.hh"
+#include "Common.hh"
+
+namespace OT {
+namespace Layout {
+namespace GSUB_impl {
+
+struct AlternateSubst
+{
+ protected:
+ union {
+ HBUINT16 format; /* Format identifier */
+ AlternateSubstFormat1_2<SmallTypes> format1;
+#ifndef HB_NO_BEYOND_64K
+ AlternateSubstFormat1_2<MediumTypes> format2;
+#endif
+ } u;
+ public:
+
+ template <typename context_t, typename ...Ts>
+ typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
+ {
+ if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value ();
+ TRACE_DISPATCH (this, u.format);
+ switch (u.format) {
+ case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
+#ifndef HB_NO_BEYOND_64K
+ case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...));
+#endif
+ default:return_trace (c->default_return_value ());
+ }
+ }
+
+ /* TODO This function is unused and not updated to 24bit GIDs. Should be done by using
+ * iterators. While at it perhaps using iterator of arrays of hb_codepoint_t instead. */
+ bool serialize (hb_serialize_context_t *c,
+ hb_sorted_array_t<const HBGlyphID16> glyphs,
+ hb_array_t<const unsigned int> alternate_len_list,
+ hb_array_t<const HBGlyphID16> alternate_glyphs_list)
+ {
+ TRACE_SERIALIZE (this);
+ if (unlikely (!c->extend_min (u.format))) return_trace (false);
+ unsigned int format = 1;
+ u.format = format;
+ switch (u.format) {
+ case 1: return_trace (u.format1.serialize (c, glyphs, alternate_len_list, alternate_glyphs_list));
+ default:return_trace (false);
+ }
+ }
+
+ /* TODO subset() should choose format. */
+
+};
+
+}
+}
+}
+
+#endif /* OT_LAYOUT_GSUB_ALTERNATESUBST_HH */
diff --git a/gfx/harfbuzz/src/OT/Layout/GSUB/AlternateSubstFormat1.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/AlternateSubstFormat1.hh
new file mode 100644
index 0000000000..adec65d586
--- /dev/null
+++ b/gfx/harfbuzz/src/OT/Layout/GSUB/AlternateSubstFormat1.hh
@@ -0,0 +1,128 @@
+#ifndef OT_LAYOUT_GSUB_ALTERNATESUBSTFORMAT1_HH
+#define OT_LAYOUT_GSUB_ALTERNATESUBSTFORMAT1_HH
+
+#include "AlternateSet.hh"
+#include "Common.hh"
+
+namespace OT {
+namespace Layout {
+namespace GSUB_impl {
+
+template <typename Types>
+struct AlternateSubstFormat1_2
+{
+ protected:
+ HBUINT16 format; /* Format identifier--format = 1 */
+ typename Types::template OffsetTo<Coverage>
+ coverage; /* Offset to Coverage table--from
+ * beginning of Substitution table */
+ Array16Of<typename Types::template OffsetTo<AlternateSet<Types>>>
+ alternateSet; /* Array of AlternateSet tables
+ * ordered by Coverage Index */
+ public:
+ DEFINE_SIZE_ARRAY (2 + 2 * Types::size, alternateSet);
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (coverage.sanitize (c, this) && alternateSet.sanitize (c, this));
+ }
+
+ bool intersects (const hb_set_t *glyphs) const
+ { return (this+coverage).intersects (glyphs); }
+
+ bool may_have_non_1to1 () const
+ { return false; }
+
+ void closure (hb_closure_context_t *c) const
+ {
+ + hb_zip (this+coverage, alternateSet)
+ | hb_filter (c->parent_active_glyphs (), hb_first)
+ | hb_map (hb_second)
+ | hb_map (hb_add (this))
+ | hb_apply ([c] (const AlternateSet<Types> &_) { _.closure (c); })
+ ;
+ }
+
+ void closure_lookups (hb_closure_lookups_context_t *c) const {}
+
+ void collect_glyphs (hb_collect_glyphs_context_t *c) const
+ {
+ if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
+ + hb_zip (this+coverage, alternateSet)
+ | hb_map (hb_second)
+ | hb_map (hb_add (this))
+ | hb_apply ([c] (const AlternateSet<Types> &_) { _.collect_glyphs (c); })
+ ;
+ }
+
+ const Coverage &get_coverage () const { return this+coverage; }
+
+ bool would_apply (hb_would_apply_context_t *c) const
+ { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; }
+
+ unsigned
+ get_glyph_alternates (hb_codepoint_t gid,
+ unsigned start_offset,
+ unsigned *alternate_count /* IN/OUT. May be NULL. */,
+ hb_codepoint_t *alternate_glyphs /* OUT. May be NULL. */) const
+ { return (this+alternateSet[(this+coverage).get_coverage (gid)])
+ .get_alternates (start_offset, alternate_count, alternate_glyphs); }
+
+ bool apply (hb_ot_apply_context_t *c) const
+ {
+ TRACE_APPLY (this);
+
+ unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
+ if (likely (index == NOT_COVERED)) return_trace (false);
+
+ return_trace ((this+alternateSet[index]).apply (c));
+ }
+
+ bool serialize (hb_serialize_context_t *c,
+ hb_sorted_array_t<const HBGlyphID16> glyphs,
+ hb_array_t<const unsigned int> alternate_len_list,
+ hb_array_t<const HBGlyphID16> alternate_glyphs_list)
+ {
+ TRACE_SERIALIZE (this);
+ if (unlikely (!c->extend_min (this))) return_trace (false);
+ if (unlikely (!alternateSet.serialize (c, glyphs.length))) return_trace (false);
+ for (unsigned int i = 0; i < glyphs.length; i++)
+ {
+ unsigned int alternate_len = alternate_len_list[i];
+ if (unlikely (!alternateSet[i]
+ .serialize_serialize (c, alternate_glyphs_list.sub_array (0, alternate_len))))
+ return_trace (false);
+ alternate_glyphs_list += alternate_len;
+ }
+ return_trace (coverage.serialize_serialize (c, glyphs));
+ }
+
+ bool subset (hb_subset_context_t *c) const
+ {
+ TRACE_SUBSET (this);
+ const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
+ const hb_map_t &glyph_map = *c->plan->glyph_map;
+
+ auto *out = c->serializer->start_embed (*this);
+ if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+ out->format = format;
+
+ hb_sorted_vector_t<hb_codepoint_t> new_coverage;
+ + hb_zip (this+coverage, alternateSet)
+ | hb_filter (glyphset, hb_first)
+ | hb_filter (subset_offset_array (c, out->alternateSet, this), hb_second)
+ | hb_map (hb_first)
+ | hb_map (glyph_map)
+ | hb_sink (new_coverage)
+ ;
+ out->coverage.serialize_serialize (c->serializer, new_coverage.iter ());
+ return_trace (bool (new_coverage));
+ }
+};
+
+}
+}
+}
+
+#endif /* OT_LAYOUT_GSUB_ALTERNATESUBSTFORMAT1_HH */
diff --git a/gfx/harfbuzz/src/OT/Layout/GSUB/ChainContextSubst.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/ChainContextSubst.hh
new file mode 100644
index 0000000000..08fd779f73
--- /dev/null
+++ b/gfx/harfbuzz/src/OT/Layout/GSUB/ChainContextSubst.hh
@@ -0,0 +1,18 @@
+#ifndef OT_LAYOUT_GSUB_CHAINCONTEXTSUBST_HH
+#define OT_LAYOUT_GSUB_CHAINCONTEXTSUBST_HH
+
+// TODO(garretrieger): move to new layout.
+#include "../../../hb-ot-layout-gsubgpos.hh"
+#include "Common.hh"
+
+namespace OT {
+namespace Layout {
+namespace GSUB_impl {
+
+struct ChainContextSubst : ChainContext {};
+
+}
+}
+}
+
+#endif /* OT_LAYOUT_GSUB_CHAINCONTEXTSUBST_HH */
diff --git a/gfx/harfbuzz/src/OT/Layout/GSUB/Common.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/Common.hh
new file mode 100644
index 0000000000..968bba0481
--- /dev/null
+++ b/gfx/harfbuzz/src/OT/Layout/GSUB/Common.hh
@@ -0,0 +1,21 @@
+#ifndef OT_LAYOUT_GSUB_COMMON_HH
+#define OT_LAYOUT_GSUB_COMMON_HH
+
+#include "../../../hb-serialize.hh"
+#include "../../../hb-ot-layout-gsubgpos.hh"
+
+namespace OT {
+namespace Layout {
+namespace GSUB_impl {
+
+typedef hb_pair_t<hb_codepoint_t, hb_codepoint_t> hb_codepoint_pair_t;
+
+template<typename Iterator>
+static void SingleSubst_serialize (hb_serialize_context_t *c,
+ Iterator it);
+
+}
+}
+}
+
+#endif /* OT_LAYOUT_GSUB_COMMON_HH */
diff --git a/gfx/harfbuzz/src/OT/Layout/GSUB/ContextSubst.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/ContextSubst.hh
new file mode 100644
index 0000000000..9f8cb46b5e
--- /dev/null
+++ b/gfx/harfbuzz/src/OT/Layout/GSUB/ContextSubst.hh
@@ -0,0 +1,18 @@
+#ifndef OT_LAYOUT_GSUB_CONTEXTSUBST_HH
+#define OT_LAYOUT_GSUB_CONTEXTSUBST_HH
+
+// TODO(garretrieger): move to new layout.
+#include "../../../hb-ot-layout-gsubgpos.hh"
+#include "Common.hh"
+
+namespace OT {
+namespace Layout {
+namespace GSUB_impl {
+
+struct ContextSubst : Context {};
+
+}
+}
+}
+
+#endif /* OT_LAYOUT_GSUB_CONTEXTSUBST_HH */
diff --git a/gfx/harfbuzz/src/OT/Layout/GSUB/ExtensionSubst.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/ExtensionSubst.hh
new file mode 100644
index 0000000000..831a7dfa2d
--- /dev/null
+++ b/gfx/harfbuzz/src/OT/Layout/GSUB/ExtensionSubst.hh
@@ -0,0 +1,22 @@
+#ifndef OT_LAYOUT_GSUB_EXTENSIONSUBST_HH
+#define OT_LAYOUT_GSUB_EXTENSIONSUBST_HH
+
+// TODO(garretrieger): move to new layout.
+#include "../../../hb-ot-layout-gsubgpos.hh"
+#include "Common.hh"
+
+namespace OT {
+namespace Layout {
+namespace GSUB_impl {
+
+struct ExtensionSubst : Extension<ExtensionSubst>
+{
+ typedef struct SubstLookupSubTable SubTable;
+ bool is_reverse () const;
+};
+
+}
+}
+}
+
+#endif /* OT_LAYOUT_GSUB_EXTENSIONSUBST_HH */
diff --git a/gfx/harfbuzz/src/OT/Layout/GSUB/GSUB.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/GSUB.hh
new file mode 100644
index 0000000000..900cf603e4
--- /dev/null
+++ b/gfx/harfbuzz/src/OT/Layout/GSUB/GSUB.hh
@@ -0,0 +1,61 @@
+#ifndef OT_LAYOUT_GSUB_GSUB_HH
+#define OT_LAYOUT_GSUB_GSUB_HH
+
+#include "../../../hb-ot-layout-gsubgpos.hh"
+#include "Common.hh"
+#include "SubstLookup.hh"
+
+namespace OT {
+
+using Layout::GSUB_impl::SubstLookup;
+
+namespace Layout {
+
+/*
+ * GSUB -- Glyph Substitution
+ * https://docs.microsoft.com/en-us/typography/opentype/spec/gsub
+ */
+
+struct GSUB : GSUBGPOS
+{
+ using Lookup = SubstLookup;
+
+ static constexpr hb_tag_t tableTag = HB_OT_TAG_GSUB;
+
+ const SubstLookup& get_lookup (unsigned int i) const
+ { return static_cast<const SubstLookup &> (GSUBGPOS::get_lookup (i)); }
+
+ bool subset (hb_subset_context_t *c) const
+ {
+ hb_subset_layout_context_t l (c, tableTag);
+ return GSUBGPOS::subset<SubstLookup> (&l);
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (GSUBGPOS::sanitize<SubstLookup> (c));
+ }
+
+ HB_INTERNAL bool is_blocklisted (hb_blob_t *blob,
+ hb_face_t *face) const;
+
+ void closure_lookups (hb_face_t *face,
+ const hb_set_t *glyphs,
+ hb_set_t *lookup_indexes /* IN/OUT */) const
+ { GSUBGPOS::closure_lookups<SubstLookup> (face, glyphs, lookup_indexes); }
+
+ typedef GSUBGPOS::accelerator_t<GSUB> accelerator_t;
+};
+
+
+}
+
+struct GSUB_accelerator_t : Layout::GSUB::accelerator_t {
+ GSUB_accelerator_t (hb_face_t *face) : Layout::GSUB::accelerator_t (face) {}
+};
+
+
+}
+
+#endif /* OT_LAYOUT_GSUB_GSUB_HH */
diff --git a/gfx/harfbuzz/src/OT/Layout/GSUB/Ligature.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/Ligature.hh
new file mode 100644
index 0000000000..8674a52fb5
--- /dev/null
+++ b/gfx/harfbuzz/src/OT/Layout/GSUB/Ligature.hh
@@ -0,0 +1,190 @@
+#ifndef OT_LAYOUT_GSUB_LIGATURE_HH
+#define OT_LAYOUT_GSUB_LIGATURE_HH
+
+#include "Common.hh"
+
+namespace OT {
+namespace Layout {
+namespace GSUB_impl {
+
+template <typename Types>
+struct Ligature
+{
+ public:
+ typename Types::HBGlyphID
+ ligGlyph; /* GlyphID of ligature to substitute */
+ HeadlessArrayOf<typename Types::HBGlyphID>
+ component; /* Array of component GlyphIDs--start
+ * with the second component--ordered
+ * in writing direction */
+ public:
+ DEFINE_SIZE_ARRAY (Types::size + 2, component);
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (ligGlyph.sanitize (c) && component.sanitize (c));
+ }
+
+ bool intersects (const hb_set_t *glyphs) const
+ { return hb_all (component, glyphs); }
+
+ bool intersects_lig_glyph (const hb_set_t *glyphs) const
+ { return glyphs->has(ligGlyph); }
+
+ void closure (hb_closure_context_t *c) const
+ {
+ if (!intersects (c->glyphs)) return;
+ c->output->add (ligGlyph);
+ }
+
+ void collect_glyphs (hb_collect_glyphs_context_t *c) const
+ {
+ c->input->add_array (component.arrayZ, component.get_length ());
+ c->output->add (ligGlyph);
+ }
+
+ bool would_apply (hb_would_apply_context_t *c) const
+ {
+ if (c->len != component.lenP1)
+ return false;
+
+ for (unsigned int i = 1; i < c->len; i++)
+ if (likely (c->glyphs[i] != component[i]))
+ return false;
+
+ return true;
+ }
+
+ bool apply (hb_ot_apply_context_t *c) const
+ {
+ TRACE_APPLY (this);
+ unsigned int count = component.lenP1;
+
+ if (unlikely (!count)) return_trace (false);
+
+ /* Special-case to make it in-place and not consider this
+ * as a "ligated" substitution. */
+ if (unlikely (count == 1))
+ {
+
+ if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+ {
+ c->buffer->sync_so_far ();
+ c->buffer->message (c->font,
+ "replacing glyph at %u (ligature substitution)",
+ c->buffer->idx);
+ }
+
+ c->replace_glyph (ligGlyph);
+
+ if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+ {
+ c->buffer->message (c->font,
+ "replaced glyph at %u (ligature substitution)",
+ c->buffer->idx - 1u);
+ }
+
+ return_trace (true);
+ }
+
+ unsigned int total_component_count = 0;
+
+ unsigned int match_end = 0;
+ unsigned int match_positions[HB_MAX_CONTEXT_LENGTH];
+
+ if (likely (!match_input (c, count,
+ &component[1],
+ match_glyph,
+ nullptr,
+ &match_end,
+ match_positions,
+ &total_component_count)))
+ {
+ c->buffer->unsafe_to_concat (c->buffer->idx, match_end);
+ return_trace (false);
+ }
+
+ unsigned pos = 0;
+ if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+ {
+ unsigned delta = c->buffer->sync_so_far ();
+
+ pos = c->buffer->idx;
+
+ char buf[HB_MAX_CONTEXT_LENGTH * 16] = {0};
+ char *p = buf;
+
+ match_end += delta;
+ for (unsigned i = 0; i < count; i++)
+ {
+ match_positions[i] += delta;
+ if (i)
+ *p++ = ',';
+ snprintf (p, sizeof(buf) - (p - buf), "%u", match_positions[i]);
+ p += strlen(p);
+ }
+
+ c->buffer->message (c->font,
+ "ligating glyphs at %s",
+ buf);
+ }
+
+ ligate_input (c,
+ count,
+ match_positions,
+ match_end,
+ ligGlyph,
+ total_component_count);
+
+ if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+ {
+ c->buffer->sync_so_far ();
+ c->buffer->message (c->font,
+ "ligated glyph at %u",
+ pos);
+ }
+
+ return_trace (true);
+ }
+
+ template <typename Iterator,
+ hb_requires (hb_is_source_of (Iterator, hb_codepoint_t))>
+ bool serialize (hb_serialize_context_t *c,
+ hb_codepoint_t ligature,
+ Iterator components /* Starting from second */)
+ {
+ TRACE_SERIALIZE (this);
+ if (unlikely (!c->extend_min (this))) return_trace (false);
+ ligGlyph = ligature;
+ if (unlikely (!component.serialize (c, components))) return_trace (false);
+ return_trace (true);
+ }
+
+ bool subset (hb_subset_context_t *c, unsigned coverage_idx) const
+ {
+ TRACE_SUBSET (this);
+ const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
+ const hb_map_t &glyph_map = *c->plan->glyph_map;
+
+ if (!intersects (&glyphset) || !glyphset.has (ligGlyph)) return_trace (false);
+ // Ensure Coverage table is always packed after this.
+ c->serializer->add_virtual_link (coverage_idx);
+
+ auto it =
+ + hb_iter (component)
+ | hb_map (glyph_map)
+ ;
+
+ auto *out = c->serializer->start_embed (*this);
+ return_trace (out->serialize (c->serializer,
+ glyph_map[ligGlyph],
+ it)); }
+};
+
+
+}
+}
+}
+
+#endif /* OT_LAYOUT_GSUB_LIGATURE_HH */
diff --git a/gfx/harfbuzz/src/OT/Layout/GSUB/LigatureSet.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/LigatureSet.hh
new file mode 100644
index 0000000000..0ba262e901
--- /dev/null
+++ b/gfx/harfbuzz/src/OT/Layout/GSUB/LigatureSet.hh
@@ -0,0 +1,189 @@
+#ifndef OT_LAYOUT_GSUB_LIGATURESET_HH
+#define OT_LAYOUT_GSUB_LIGATURESET_HH
+
+#include "Common.hh"
+#include "Ligature.hh"
+
+namespace OT {
+namespace Layout {
+namespace GSUB_impl {
+
+template <typename Types>
+struct LigatureSet
+{
+ protected:
+ Array16OfOffset16To<Ligature<Types>>
+ ligature; /* Array LigatureSet tables
+ * ordered by preference */
+ public:
+ DEFINE_SIZE_ARRAY (2, ligature);
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (ligature.sanitize (c, this));
+ }
+
+ bool intersects (const hb_set_t *glyphs) const
+ {
+ return
+ + hb_iter (ligature)
+ | hb_map (hb_add (this))
+ | hb_map ([glyphs] (const Ligature<Types> &_) { return _.intersects (glyphs); })
+ | hb_any
+ ;
+ }
+
+ bool intersects_lig_glyph (const hb_set_t *glyphs) const
+ {
+ return
+ + hb_iter (ligature)
+ | hb_map (hb_add (this))
+ | hb_map ([glyphs] (const Ligature<Types> &_) {
+ return _.intersects_lig_glyph (glyphs) && _.intersects (glyphs);
+ })
+ | hb_any
+ ;
+ }
+
+ void closure (hb_closure_context_t *c) const
+ {
+ + hb_iter (ligature)
+ | hb_map (hb_add (this))
+ | hb_apply ([c] (const Ligature<Types> &_) { _.closure (c); })
+ ;
+ }
+
+ void collect_glyphs (hb_collect_glyphs_context_t *c) const
+ {
+ + hb_iter (ligature)
+ | hb_map (hb_add (this))
+ | hb_apply ([c] (const Ligature<Types> &_) { _.collect_glyphs (c); })
+ ;
+ }
+
+ bool would_apply (hb_would_apply_context_t *c) const
+ {
+ return
+ + hb_iter (ligature)
+ | hb_map (hb_add (this))
+ | hb_map ([c] (const Ligature<Types> &_) { return _.would_apply (c); })
+ | hb_any
+ ;
+ }
+
+ static bool match_always (hb_glyph_info_t &info HB_UNUSED, unsigned value HB_UNUSED, const void *data HB_UNUSED)
+ {
+ return true;
+ }
+
+ bool apply (hb_ot_apply_context_t *c) const
+ {
+ TRACE_APPLY (this);
+
+ unsigned int num_ligs = ligature.len;
+
+#ifndef HB_NO_OT_LIGATURES_FAST_PATH
+ if (HB_OPTIMIZE_SIZE_VAL || num_ligs <= 2)
+#endif
+ {
+ slow:
+ for (unsigned int i = 0; i < num_ligs; i++)
+ {
+ const auto &lig = this+ligature.arrayZ[i];
+ if (lig.apply (c)) return_trace (true);
+ }
+ return_trace (false);
+ }
+
+ /* This version is optimized for speed by matching the first component
+ * of the ligature here, instead of calling into the ligation code. */
+
+ hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
+ skippy_iter.reset (c->buffer->idx, 1);
+ skippy_iter.set_match_func (match_always, nullptr);
+ skippy_iter.set_glyph_data ((HBUINT16 *) nullptr);
+ unsigned unsafe_to;
+ hb_codepoint_t first = (unsigned) -1;
+ bool matched = skippy_iter.next (&unsafe_to);
+ if (likely (matched))
+ {
+ first = c->buffer->info[skippy_iter.idx].codepoint;
+ unsafe_to = skippy_iter.idx + 1;
+
+ if (skippy_iter.may_skip (c->buffer->info[skippy_iter.idx]))
+ {
+ /* Can't use the fast path if eg. the next char is a default-ignorable
+ * or other skippable. */
+ goto slow;
+ }
+ }
+
+ bool unsafe_to_concat = false;
+
+ for (unsigned int i = 0; i < num_ligs; i++)
+ {
+ const auto &lig = this+ligature.arrayZ[i];
+ if (unlikely (lig.component.lenP1 <= 1) ||
+ lig.component[1] == first)
+ {
+ if (lig.apply (c))
+ {
+ if (unsafe_to_concat)
+ c->buffer->unsafe_to_concat (c->buffer->idx, unsafe_to);
+ return_trace (true);
+ }
+ }
+ else if (likely (lig.component.lenP1 > 1))
+ unsafe_to_concat = true;
+ }
+ if (likely (unsafe_to_concat))
+ c->buffer->unsafe_to_concat (c->buffer->idx, unsafe_to);
+
+ return_trace (false);
+ }
+
+ bool serialize (hb_serialize_context_t *c,
+ hb_array_t<const HBGlyphID16> ligatures,
+ hb_array_t<const unsigned int> component_count_list,
+ hb_array_t<const HBGlyphID16> &component_list /* Starting from second for each ligature */)
+ {
+ TRACE_SERIALIZE (this);
+ if (unlikely (!c->extend_min (this))) return_trace (false);
+ if (unlikely (!ligature.serialize (c, ligatures.length))) return_trace (false);
+ for (unsigned int i = 0; i < ligatures.length; i++)
+ {
+ unsigned int component_count = (unsigned) hb_max ((int) component_count_list[i] - 1, 0);
+ if (unlikely (!ligature[i].serialize_serialize (c,
+ ligatures[i],
+ component_list.sub_array (0, component_count))))
+ return_trace (false);
+ component_list += component_count;
+ }
+ return_trace (true);
+ }
+
+ bool subset (hb_subset_context_t *c, unsigned coverage_idx) const
+ {
+ TRACE_SUBSET (this);
+ auto *out = c->serializer->start_embed (*this);
+ if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+
+ + hb_iter (ligature)
+ | hb_filter (subset_offset_array (c, out->ligature, this, coverage_idx))
+ | hb_drain
+ ;
+
+ if (bool (out->ligature))
+ // Ensure Coverage table is always packed after this.
+ c->serializer->add_virtual_link (coverage_idx);
+
+ return_trace (bool (out->ligature));
+ }
+};
+
+}
+}
+}
+
+#endif /* OT_LAYOUT_GSUB_LIGATURESET_HH */
diff --git a/gfx/harfbuzz/src/OT/Layout/GSUB/LigatureSubst.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/LigatureSubst.hh
new file mode 100644
index 0000000000..18f6e35581
--- /dev/null
+++ b/gfx/harfbuzz/src/OT/Layout/GSUB/LigatureSubst.hh
@@ -0,0 +1,71 @@
+#ifndef OT_LAYOUT_GSUB_LIGATURESUBST_HH
+#define OT_LAYOUT_GSUB_LIGATURESUBST_HH
+
+#include "Common.hh"
+#include "LigatureSubstFormat1.hh"
+
+namespace OT {
+namespace Layout {
+namespace GSUB_impl {
+
+struct LigatureSubst
+{
+ protected:
+ union {
+ HBUINT16 format; /* Format identifier */
+ LigatureSubstFormat1_2<SmallTypes> format1;
+#ifndef HB_NO_BEYOND_64K
+ LigatureSubstFormat1_2<MediumTypes> format2;
+#endif
+ } u;
+
+ public:
+ template <typename context_t, typename ...Ts>
+ typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
+ {
+ if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value ();
+ TRACE_DISPATCH (this, u.format);
+ switch (u.format) {
+ case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
+#ifndef HB_NO_BEYOND_64K
+ case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...));
+#endif
+ default:return_trace (c->default_return_value ());
+ }
+ }
+
+ /* TODO This function is only used by small GIDs, and not updated to 24bit GIDs. Should
+ * be done by using iterators. While at it perhaps using iterator of arrays of hb_codepoint_t
+ * instead. */
+ bool serialize (hb_serialize_context_t *c,
+ hb_sorted_array_t<const HBGlyphID16> first_glyphs,
+ hb_array_t<const unsigned int> ligature_per_first_glyph_count_list,
+ hb_array_t<const HBGlyphID16> ligatures_list,
+ hb_array_t<const unsigned int> component_count_list,
+ hb_array_t<const HBGlyphID16> component_list /* Starting from second for each ligature */)
+ {
+ TRACE_SERIALIZE (this);
+ if (unlikely (!c->extend_min (u.format))) return_trace (false);
+ unsigned int format = 1;
+ u.format = format;
+ switch (u.format) {
+ case 1: return_trace (u.format1.serialize (c,
+ first_glyphs,
+ ligature_per_first_glyph_count_list,
+ ligatures_list,
+ component_count_list,
+ component_list));
+ default:return_trace (false);
+ }
+ }
+
+ /* TODO subset() should choose format. */
+
+};
+
+
+}
+}
+}
+
+#endif /* OT_LAYOUT_GSUB_LIGATURESUBST_HH */
diff --git a/gfx/harfbuzz/src/OT/Layout/GSUB/LigatureSubstFormat1.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/LigatureSubstFormat1.hh
new file mode 100644
index 0000000000..5c7df97d13
--- /dev/null
+++ b/gfx/harfbuzz/src/OT/Layout/GSUB/LigatureSubstFormat1.hh
@@ -0,0 +1,166 @@
+#ifndef OT_LAYOUT_GSUB_LIGATURESUBSTFORMAT1_HH
+#define OT_LAYOUT_GSUB_LIGATURESUBSTFORMAT1_HH
+
+#include "Common.hh"
+#include "LigatureSet.hh"
+
+namespace OT {
+namespace Layout {
+namespace GSUB_impl {
+
+template <typename Types>
+struct LigatureSubstFormat1_2
+{
+ protected:
+ HBUINT16 format; /* Format identifier--format = 1 */
+ typename Types::template OffsetTo<Coverage>
+ coverage; /* Offset to Coverage table--from
+ * beginning of Substitution table */
+ Array16Of<typename Types::template OffsetTo<LigatureSet<Types>>>
+ ligatureSet; /* Array LigatureSet tables
+ * ordered by Coverage Index */
+ public:
+ DEFINE_SIZE_ARRAY (4 + Types::size, ligatureSet);
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (coverage.sanitize (c, this) && ligatureSet.sanitize (c, this));
+ }
+
+ bool intersects (const hb_set_t *glyphs) const
+ {
+ return
+ + hb_zip (this+coverage, ligatureSet)
+ | hb_filter (*glyphs, hb_first)
+ | hb_map (hb_second)
+ | hb_map ([this, glyphs] (const typename Types::template OffsetTo<LigatureSet<Types>> &_)
+ { return (this+_).intersects (glyphs); })
+ | hb_any
+ ;
+ }
+
+ bool may_have_non_1to1 () const
+ { return true; }
+
+ void closure (hb_closure_context_t *c) const
+ {
+ + hb_zip (this+coverage, ligatureSet)
+ | hb_filter (c->parent_active_glyphs (), hb_first)
+ | hb_map (hb_second)
+ | hb_map (hb_add (this))
+ | hb_apply ([c] (const LigatureSet<Types> &_) { _.closure (c); })
+ ;
+
+ }
+
+ void closure_lookups (hb_closure_lookups_context_t *c) const {}
+
+ void collect_glyphs (hb_collect_glyphs_context_t *c) const
+ {
+ if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
+
+ + hb_zip (this+coverage, ligatureSet)
+ | hb_map (hb_second)
+ | hb_map (hb_add (this))
+ | hb_apply ([c] (const LigatureSet<Types> &_) { _.collect_glyphs (c); })
+ ;
+ }
+
+ const Coverage &get_coverage () const { return this+coverage; }
+
+ bool would_apply (hb_would_apply_context_t *c) const
+ {
+ unsigned int index = (this+coverage).get_coverage (c->glyphs[0]);
+ if (likely (index == NOT_COVERED)) return false;
+
+ const auto &lig_set = this+ligatureSet[index];
+ return lig_set.would_apply (c);
+ }
+
+ bool apply (hb_ot_apply_context_t *c) const
+ {
+ TRACE_APPLY (this);
+
+ unsigned int index = (this+coverage).get_coverage (c->buffer->cur ().codepoint);
+ if (likely (index == NOT_COVERED)) return_trace (false);
+
+ const auto &lig_set = this+ligatureSet[index];
+ return_trace (lig_set.apply (c));
+ }
+
+ bool serialize (hb_serialize_context_t *c,
+ hb_sorted_array_t<const HBGlyphID16> first_glyphs,
+ hb_array_t<const unsigned int> ligature_per_first_glyph_count_list,
+ hb_array_t<const HBGlyphID16> ligatures_list,
+ hb_array_t<const unsigned int> component_count_list,
+ hb_array_t<const HBGlyphID16> component_list /* Starting from second for each ligature */)
+ {
+ TRACE_SERIALIZE (this);
+ if (unlikely (!c->extend_min (this))) return_trace (false);
+ if (unlikely (!ligatureSet.serialize (c, first_glyphs.length))) return_trace (false);
+ for (unsigned int i = 0; i < first_glyphs.length; i++)
+ {
+ unsigned int ligature_count = ligature_per_first_glyph_count_list[i];
+ if (unlikely (!ligatureSet[i]
+ .serialize_serialize (c,
+ ligatures_list.sub_array (0, ligature_count),
+ component_count_list.sub_array (0, ligature_count),
+ component_list))) return_trace (false);
+ ligatures_list += ligature_count;
+ component_count_list += ligature_count;
+ }
+ return_trace (coverage.serialize_serialize (c, first_glyphs));
+ }
+
+ bool subset (hb_subset_context_t *c) const
+ {
+ TRACE_SUBSET (this);
+ const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
+ const hb_map_t &glyph_map = *c->plan->glyph_map;
+
+ auto *out = c->serializer->start_embed (*this);
+ if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+ out->format = format;
+
+ // Due to a bug in some older versions of windows 7 the Coverage table must be
+ // packed after the LigatureSet and Ligature tables, so serialize Coverage first
+ // which places it last in the packed order.
+ hb_set_t new_coverage;
+ + hb_zip (this+coverage, hb_iter (ligatureSet) | hb_map (hb_add (this)))
+ | hb_filter (glyphset, hb_first)
+ | hb_filter ([&] (const LigatureSet<Types>& _) {
+ return _.intersects_lig_glyph (&glyphset);
+ }, hb_second)
+ | hb_map (hb_first)
+ | hb_sink (new_coverage);
+
+ if (!c->serializer->push<Coverage> ()
+ ->serialize (c->serializer,
+ + new_coverage.iter () | hb_map_retains_sorting (glyph_map)))
+ {
+ c->serializer->pop_discard ();
+ return_trace (false);
+ }
+
+ unsigned coverage_idx = c->serializer->pop_pack ();
+ c->serializer->add_link (out->coverage, coverage_idx);
+
+ + hb_zip (this+coverage, ligatureSet)
+ | hb_filter (new_coverage, hb_first)
+ | hb_map (hb_second)
+ // to ensure that the repacker always orders the coverage table after the LigatureSet
+ // and LigatureSubtable's they will be linked to the Coverage table via a virtual link
+ // the coverage table object idx is passed down to facilitate this.
+ | hb_apply (subset_offset_array (c, out->ligatureSet, this, coverage_idx))
+ ;
+
+ return_trace (bool (new_coverage));
+ }
+};
+
+}
+}
+}
+
+#endif /* OT_LAYOUT_GSUB_LIGATURESUBSTFORMAT1_HH */
diff --git a/gfx/harfbuzz/src/OT/Layout/GSUB/MultipleSubst.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/MultipleSubst.hh
new file mode 100644
index 0000000000..742c8587ee
--- /dev/null
+++ b/gfx/harfbuzz/src/OT/Layout/GSUB/MultipleSubst.hh
@@ -0,0 +1,62 @@
+#ifndef OT_LAYOUT_GSUB_MULTIPLESUBST_HH
+#define OT_LAYOUT_GSUB_MULTIPLESUBST_HH
+
+#include "Common.hh"
+#include "MultipleSubstFormat1.hh"
+
+namespace OT {
+namespace Layout {
+namespace GSUB_impl {
+
+struct MultipleSubst
+{
+ protected:
+ union {
+ HBUINT16 format; /* Format identifier */
+ MultipleSubstFormat1_2<SmallTypes> format1;
+#ifndef HB_NO_BEYOND_64K
+ MultipleSubstFormat1_2<MediumTypes> format2;
+#endif
+ } u;
+
+ public:
+
+ template <typename context_t, typename ...Ts>
+ typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
+ {
+ if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value ();
+ TRACE_DISPATCH (this, u.format);
+ switch (u.format) {
+ case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
+#ifndef HB_NO_BEYOND_64K
+ case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...));
+#endif
+ default:return_trace (c->default_return_value ());
+ }
+ }
+
+ template<typename Iterator,
+ hb_requires (hb_is_sorted_iterator (Iterator))>
+ bool serialize (hb_serialize_context_t *c,
+ Iterator it)
+ {
+ TRACE_SERIALIZE (this);
+ if (unlikely (!c->extend_min (u.format))) return_trace (false);
+ unsigned int format = 1;
+ u.format = format;
+ switch (u.format) {
+ case 1: return_trace (u.format1.serialize (c, it));
+ default:return_trace (false);
+ }
+ }
+
+ /* TODO subset() should choose format. */
+
+};
+
+
+}
+}
+}
+
+#endif /* OT_LAYOUT_GSUB_MULTIPLESUBST_HH */
diff --git a/gfx/harfbuzz/src/OT/Layout/GSUB/MultipleSubstFormat1.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/MultipleSubstFormat1.hh
new file mode 100644
index 0000000000..3b4bd11694
--- /dev/null
+++ b/gfx/harfbuzz/src/OT/Layout/GSUB/MultipleSubstFormat1.hh
@@ -0,0 +1,130 @@
+#ifndef OT_LAYOUT_GSUB_MULTIPLESUBSTFORMAT1_HH
+#define OT_LAYOUT_GSUB_MULTIPLESUBSTFORMAT1_HH
+
+#include "Common.hh"
+#include "Sequence.hh"
+
+namespace OT {
+namespace Layout {
+namespace GSUB_impl {
+
+template <typename Types>
+struct MultipleSubstFormat1_2
+{
+ protected:
+ HBUINT16 format; /* Format identifier--format = 1 */
+ typename Types::template OffsetTo<Coverage>
+ coverage; /* Offset to Coverage table--from
+ * beginning of Substitution table */
+ Array16Of<typename Types::template OffsetTo<Sequence<Types>>>
+ sequence; /* Array of Sequence tables
+ * ordered by Coverage Index */
+ public:
+ DEFINE_SIZE_ARRAY (4 + Types::size, sequence);
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (coverage.sanitize (c, this) && sequence.sanitize (c, this));
+ }
+
+ bool intersects (const hb_set_t *glyphs) const
+ { return (this+coverage).intersects (glyphs); }
+
+ bool may_have_non_1to1 () const
+ { return true; }
+
+ void closure (hb_closure_context_t *c) const
+ {
+ + hb_zip (this+coverage, sequence)
+ | hb_filter (c->parent_active_glyphs (), hb_first)
+ | hb_map (hb_second)
+ | hb_map (hb_add (this))
+ | hb_apply ([c] (const Sequence<Types> &_) { _.closure (c); })
+ ;
+ }
+
+ void closure_lookups (hb_closure_lookups_context_t *c) const {}
+
+ void collect_glyphs (hb_collect_glyphs_context_t *c) const
+ {
+ if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
+ + hb_zip (this+coverage, sequence)
+ | hb_map (hb_second)
+ | hb_map (hb_add (this))
+ | hb_apply ([c] (const Sequence<Types> &_) { _.collect_glyphs (c); })
+ ;
+ }
+
+ const Coverage &get_coverage () const { return this+coverage; }
+
+ bool would_apply (hb_would_apply_context_t *c) const
+ { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; }
+
+ bool apply (hb_ot_apply_context_t *c) const
+ {
+ TRACE_APPLY (this);
+
+ unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
+ if (likely (index == NOT_COVERED)) return_trace (false);
+
+ return_trace ((this+sequence[index]).apply (c));
+ }
+
+ template<typename Iterator,
+ hb_requires (hb_is_sorted_iterator (Iterator))>
+ bool serialize (hb_serialize_context_t *c,
+ Iterator it)
+ {
+ TRACE_SERIALIZE (this);
+ auto sequences =
+ + it
+ | hb_map (hb_second)
+ ;
+ auto glyphs =
+ + it
+ | hb_map_retains_sorting (hb_first)
+ ;
+ if (unlikely (!c->extend_min (this))) return_trace (false);
+
+ if (unlikely (!sequence.serialize (c, sequences.length))) return_trace (false);
+
+ for (auto& pair : hb_zip (sequences, sequence))
+ {
+ if (unlikely (!pair.second
+ .serialize_serialize (c, pair.first)))
+ return_trace (false);
+ }
+
+ return_trace (coverage.serialize_serialize (c, glyphs));
+ }
+
+ bool subset (hb_subset_context_t *c) const
+ {
+ TRACE_SUBSET (this);
+ const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
+ const hb_map_t &glyph_map = *c->plan->glyph_map;
+
+ auto *out = c->serializer->start_embed (*this);
+ if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+ out->format = format;
+
+ hb_sorted_vector_t<hb_codepoint_t> new_coverage;
+ + hb_zip (this+coverage, sequence)
+ | hb_filter (glyphset, hb_first)
+ | hb_filter (subset_offset_array (c, out->sequence, this), hb_second)
+ | hb_map (hb_first)
+ | hb_map (glyph_map)
+ | hb_sink (new_coverage)
+ ;
+ out->coverage.serialize_serialize (c->serializer, new_coverage.iter ());
+ return_trace (bool (new_coverage));
+ }
+};
+
+}
+}
+}
+
+
+#endif /* OT_LAYOUT_GSUB_MULTIPLESUBSTFORMAT1_HH */
diff --git a/gfx/harfbuzz/src/OT/Layout/GSUB/ReverseChainSingleSubst.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/ReverseChainSingleSubst.hh
new file mode 100644
index 0000000000..5ad463fea7
--- /dev/null
+++ b/gfx/harfbuzz/src/OT/Layout/GSUB/ReverseChainSingleSubst.hh
@@ -0,0 +1,36 @@
+#ifndef OT_LAYOUT_GSUB_REVERSECHAINSINGLESUBST_HH
+#define OT_LAYOUT_GSUB_REVERSECHAINSINGLESUBST_HH
+
+#include "Common.hh"
+#include "ReverseChainSingleSubstFormat1.hh"
+
+namespace OT {
+namespace Layout {
+namespace GSUB_impl {
+
+struct ReverseChainSingleSubst
+{
+ protected:
+ union {
+ HBUINT16 format; /* Format identifier */
+ ReverseChainSingleSubstFormat1 format1;
+ } u;
+
+ public:
+ template <typename context_t, typename ...Ts>
+ typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
+ {
+ if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value ();
+ TRACE_DISPATCH (this, u.format);
+ switch (u.format) {
+ case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
+ default:return_trace (c->default_return_value ());
+ }
+ }
+};
+
+}
+}
+}
+
+#endif /* HB_OT_LAYOUT_GSUB_REVERSECHAINSINGLESUBST_HH */
diff --git a/gfx/harfbuzz/src/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh
new file mode 100644
index 0000000000..2c2e1aa44f
--- /dev/null
+++ b/gfx/harfbuzz/src/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh
@@ -0,0 +1,244 @@
+#ifndef OT_LAYOUT_GSUB_REVERSECHAINSINGLESUBSTFORMAT1_HH
+#define OT_LAYOUT_GSUB_REVERSECHAINSINGLESUBSTFORMAT1_HH
+
+#include "Common.hh"
+
+namespace OT {
+namespace Layout {
+namespace GSUB_impl {
+
+struct ReverseChainSingleSubstFormat1
+{
+ protected:
+ HBUINT16 format; /* Format identifier--format = 1 */
+ Offset16To<Coverage>
+ coverage; /* Offset to Coverage table--from
+ * beginning of table */
+ Array16OfOffset16To<Coverage>
+ backtrack; /* Array of coverage tables
+ * in backtracking sequence, in glyph
+ * sequence order */
+ Array16OfOffset16To<Coverage>
+ lookaheadX; /* Array of coverage tables
+ * in lookahead sequence, in glyph
+ * sequence order */
+ Array16Of<HBGlyphID16>
+ substituteX; /* Array of substitute
+ * GlyphIDs--ordered by Coverage Index */
+ public:
+ DEFINE_SIZE_MIN (10);
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ if (!(coverage.sanitize (c, this) && backtrack.sanitize (c, this)))
+ return_trace (false);
+ const auto &lookahead = StructAfter<decltype (lookaheadX)> (backtrack);
+ if (!lookahead.sanitize (c, this))
+ return_trace (false);
+ const auto &substitute = StructAfter<decltype (substituteX)> (lookahead);
+ return_trace (substitute.sanitize (c));
+ }
+
+ bool intersects (const hb_set_t *glyphs) const
+ {
+ if (!(this+coverage).intersects (glyphs))
+ return false;
+
+ const auto &lookahead = StructAfter<decltype (lookaheadX)> (backtrack);
+
+ unsigned int count;
+
+ count = backtrack.len;
+ for (unsigned int i = 0; i < count; i++)
+ if (!(this+backtrack[i]).intersects (glyphs))
+ return false;
+
+ count = lookahead.len;
+ for (unsigned int i = 0; i < count; i++)
+ if (!(this+lookahead[i]).intersects (glyphs))
+ return false;
+
+ return true;
+ }
+
+ bool may_have_non_1to1 () const
+ { return false; }
+
+ void closure (hb_closure_context_t *c) const
+ {
+ if (!intersects (c->glyphs)) return;
+
+ const auto &lookahead = StructAfter<decltype (lookaheadX)> (backtrack);
+ const auto &substitute = StructAfter<decltype (substituteX)> (lookahead);
+
+ + hb_zip (this+coverage, substitute)
+ | hb_filter (c->parent_active_glyphs (), hb_first)
+ | hb_map (hb_second)
+ | hb_sink (c->output)
+ ;
+ }
+
+ void closure_lookups (hb_closure_lookups_context_t *c) const {}
+
+ void collect_glyphs (hb_collect_glyphs_context_t *c) const
+ {
+ if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
+
+ unsigned int count;
+
+ count = backtrack.len;
+ for (unsigned int i = 0; i < count; i++)
+ if (unlikely (!(this+backtrack[i]).collect_coverage (c->before))) return;
+
+ const auto &lookahead = StructAfter<decltype (lookaheadX)> (backtrack);
+ count = lookahead.len;
+ for (unsigned int i = 0; i < count; i++)
+ if (unlikely (!(this+lookahead[i]).collect_coverage (c->after))) return;
+
+ const auto &substitute = StructAfter<decltype (substituteX)> (lookahead);
+ count = substitute.len;
+ c->output->add_array (substitute.arrayZ, substitute.len);
+ }
+
+ const Coverage &get_coverage () const { return this+coverage; }
+
+ bool would_apply (hb_would_apply_context_t *c) const
+ { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; }
+
+ bool apply (hb_ot_apply_context_t *c) const
+ {
+ TRACE_APPLY (this);
+ if (unlikely (c->nesting_level_left != HB_MAX_NESTING_LEVEL))
+ return_trace (false); /* No chaining to this type */
+
+ unsigned int index = (this+coverage).get_coverage (c->buffer->cur ().codepoint);
+ if (likely (index == NOT_COVERED)) return_trace (false);
+
+ const auto &lookahead = StructAfter<decltype (lookaheadX)> (backtrack);
+ const auto &substitute = StructAfter<decltype (substituteX)> (lookahead);
+
+ if (unlikely (index >= substitute.len)) return_trace (false);
+
+ unsigned int start_index = 0, end_index = 0;
+ if (match_backtrack (c,
+ backtrack.len, (HBUINT16 *) backtrack.arrayZ,
+ match_coverage, this,
+ &start_index) &&
+ match_lookahead (c,
+ lookahead.len, (HBUINT16 *) lookahead.arrayZ,
+ match_coverage, this,
+ c->buffer->idx + 1, &end_index))
+ {
+ c->buffer->unsafe_to_break_from_outbuffer (start_index, end_index);
+
+ if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+ {
+ c->buffer->message (c->font,
+ "replacing glyph at %u (reverse chaining substitution)",
+ c->buffer->idx);
+ }
+
+ c->replace_glyph_inplace (substitute[index]);
+
+ if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+ {
+ c->buffer->message (c->font,
+ "replaced glyph at %u (reverse chaining substitution)",
+ c->buffer->idx);
+ }
+
+ /* Note: We DON'T decrease buffer->idx. The main loop does it
+ * for us. This is useful for preventing surprises if someone
+ * calls us through a Context lookup. */
+ return_trace (true);
+ }
+ else
+ {
+ c->buffer->unsafe_to_concat_from_outbuffer (start_index, end_index);
+ return_trace (false);
+ }
+ }
+
+ template<typename Iterator,
+ hb_requires (hb_is_iterator (Iterator))>
+ bool serialize_coverage_offset_array (hb_subset_context_t *c, Iterator it) const
+ {
+ TRACE_SERIALIZE (this);
+ auto *out = c->serializer->start_embed<Array16OfOffset16To<Coverage>> ();
+
+ if (unlikely (!c->serializer->allocate_size<HBUINT16> (HBUINT16::static_size)))
+ return_trace (false);
+
+ for (auto& offset : it) {
+ auto *o = out->serialize_append (c->serializer);
+ if (unlikely (!o) || !o->serialize_subset (c, offset, this))
+ return_trace (false);
+ }
+
+ return_trace (true);
+ }
+
+ template<typename Iterator, typename BacktrackIterator, typename LookaheadIterator,
+ hb_requires (hb_is_sorted_source_of (Iterator, hb_codepoint_pair_t)),
+ hb_requires (hb_is_iterator (BacktrackIterator)),
+ hb_requires (hb_is_iterator (LookaheadIterator))>
+ bool serialize (hb_subset_context_t *c,
+ Iterator coverage_subst_iter,
+ BacktrackIterator backtrack_iter,
+ LookaheadIterator lookahead_iter) const
+ {
+ TRACE_SERIALIZE (this);
+
+ auto *out = c->serializer->start_embed (this);
+ if (unlikely (!c->serializer->check_success (out))) return_trace (false);
+ if (unlikely (!c->serializer->embed (this->format))) return_trace (false);
+ if (unlikely (!c->serializer->embed (this->coverage))) return_trace (false);
+
+ if (!serialize_coverage_offset_array (c, backtrack_iter)) return_trace (false);
+ if (!serialize_coverage_offset_array (c, lookahead_iter)) return_trace (false);
+
+ auto *substitute_out = c->serializer->start_embed<Array16Of<HBGlyphID16>> ();
+ auto substitutes =
+ + coverage_subst_iter
+ | hb_map (hb_second)
+ ;
+
+ auto glyphs =
+ + coverage_subst_iter
+ | hb_map_retains_sorting (hb_first)
+ ;
+ if (unlikely (! c->serializer->check_success (substitute_out->serialize (c->serializer, substitutes))))
+ return_trace (false);
+
+ if (unlikely (!out->coverage.serialize_serialize (c->serializer, glyphs)))
+ return_trace (false);
+ return_trace (true);
+ }
+
+ bool subset (hb_subset_context_t *c) const
+ {
+ TRACE_SUBSET (this);
+ const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
+ const hb_map_t &glyph_map = *c->plan->glyph_map;
+
+ const auto &lookahead = StructAfter<decltype (lookaheadX)> (backtrack);
+ const auto &substitute = StructAfter<decltype (substituteX)> (lookahead);
+
+ auto it =
+ + hb_zip (this+coverage, substitute)
+ | hb_filter (glyphset, hb_first)
+ | hb_filter (glyphset, hb_second)
+ | hb_map_retains_sorting ([&] (hb_pair_t<hb_codepoint_t, const HBGlyphID16 &> p) -> hb_codepoint_pair_t
+ { return hb_pair (glyph_map[p.first], glyph_map[p.second]); })
+ ;
+
+ return_trace (bool (it) && serialize (c, it, backtrack.iter (), lookahead.iter ()));
+ }
+};
+
+}
+}
+}
+
+#endif /* HB_OT_LAYOUT_GSUB_REVERSECHAINSINGLESUBSTFORMAT1_HH */
diff --git a/gfx/harfbuzz/src/OT/Layout/GSUB/Sequence.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/Sequence.hh
new file mode 100644
index 0000000000..ae3292f329
--- /dev/null
+++ b/gfx/harfbuzz/src/OT/Layout/GSUB/Sequence.hh
@@ -0,0 +1,165 @@
+#ifndef OT_LAYOUT_GSUB_SEQUENCE_HH
+#define OT_LAYOUT_GSUB_SEQUENCE_HH
+
+#include "Common.hh"
+
+namespace OT {
+namespace Layout {
+namespace GSUB_impl {
+
+template <typename Types>
+struct Sequence
+{
+ protected:
+ Array16Of<typename Types::HBGlyphID>
+ substitute; /* String of GlyphIDs to substitute */
+ public:
+ DEFINE_SIZE_ARRAY (2, substitute);
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (substitute.sanitize (c));
+ }
+
+ bool intersects (const hb_set_t *glyphs) const
+ { return hb_all (substitute, glyphs); }
+
+ void closure (hb_closure_context_t *c) const
+ { c->output->add_array (substitute.arrayZ, substitute.len); }
+
+ void collect_glyphs (hb_collect_glyphs_context_t *c) const
+ { c->output->add_array (substitute.arrayZ, substitute.len); }
+
+ bool apply (hb_ot_apply_context_t *c) const
+ {
+ TRACE_APPLY (this);
+ unsigned int count = substitute.len;
+
+ /* Special-case to make it in-place and not consider this
+ * as a "multiplied" substitution. */
+ if (unlikely (count == 1))
+ {
+ if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+ {
+ c->buffer->sync_so_far ();
+ c->buffer->message (c->font,
+ "replacing glyph at %u (multiple substitution)",
+ c->buffer->idx);
+ }
+
+ c->replace_glyph (substitute.arrayZ[0]);
+
+ if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+ {
+ c->buffer->message (c->font,
+ "replaced glyph at %u (multiple subtitution)",
+ c->buffer->idx - 1u);
+ }
+
+ return_trace (true);
+ }
+ /* Spec disallows this, but Uniscribe allows it.
+ * https://github.com/harfbuzz/harfbuzz/issues/253 */
+ else if (unlikely (count == 0))
+ {
+ if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+ {
+ c->buffer->sync_so_far ();
+ c->buffer->message (c->font,
+ "deleting glyph at %u (multiple substitution)",
+ c->buffer->idx);
+ }
+
+ c->buffer->delete_glyph ();
+
+ if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+ {
+ c->buffer->sync_so_far ();
+ c->buffer->message (c->font,
+ "deleted glyph at %u (multiple substitution)",
+ c->buffer->idx);
+ }
+
+ return_trace (true);
+ }
+
+ if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+ {
+ c->buffer->sync_so_far ();
+ c->buffer->message (c->font,
+ "multiplying glyph at %u",
+ c->buffer->idx);
+ }
+
+ unsigned int klass = _hb_glyph_info_is_ligature (&c->buffer->cur()) ?
+ HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH : 0;
+ unsigned lig_id = _hb_glyph_info_get_lig_id (&c->buffer->cur());
+
+ for (unsigned int i = 0; i < count; i++)
+ {
+ /* If is attached to a ligature, don't disturb that.
+ * https://github.com/harfbuzz/harfbuzz/issues/3069 */
+ if (!lig_id)
+ _hb_glyph_info_set_lig_props_for_component (&c->buffer->cur(), i);
+ c->output_glyph_for_component (substitute.arrayZ[i], klass);
+ }
+ c->buffer->skip_glyph ();
+
+ if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+ {
+ c->buffer->sync_so_far ();
+
+ char buf[HB_MAX_CONTEXT_LENGTH * 16] = {0};
+ char *p = buf;
+
+ for (unsigned i = c->buffer->idx - count; i < c->buffer->idx; i++)
+ {
+ if (buf < p)
+ *p++ = ',';
+ snprintf (p, sizeof(buf) - (p - buf), "%u", i);
+ p += strlen(p);
+ }
+
+ c->buffer->message (c->font,
+ "multiplied glyphs at %s",
+ buf);
+ }
+
+ return_trace (true);
+ }
+
+ template <typename Iterator,
+ hb_requires (hb_is_source_of (Iterator, hb_codepoint_t))>
+ bool serialize (hb_serialize_context_t *c,
+ Iterator subst)
+ {
+ TRACE_SERIALIZE (this);
+ return_trace (substitute.serialize (c, subst));
+ }
+
+ bool subset (hb_subset_context_t *c) const
+ {
+ TRACE_SUBSET (this);
+ const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
+ const hb_map_t &glyph_map = *c->plan->glyph_map;
+
+ if (!intersects (&glyphset)) return_trace (false);
+
+ auto it =
+ + hb_iter (substitute)
+ | hb_map (glyph_map)
+ ;
+
+ auto *out = c->serializer->start_embed (*this);
+ return_trace (out->serialize (c->serializer, it));
+ }
+};
+
+
+}
+}
+}
+
+
+#endif /* OT_LAYOUT_GSUB_SEQUENCE_HH */
diff --git a/gfx/harfbuzz/src/OT/Layout/GSUB/SingleSubst.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/SingleSubst.hh
new file mode 100644
index 0000000000..181c9e52e5
--- /dev/null
+++ b/gfx/harfbuzz/src/OT/Layout/GSUB/SingleSubst.hh
@@ -0,0 +1,103 @@
+#ifndef OT_LAYOUT_GSUB_SINGLESUBST_HH
+#define OT_LAYOUT_GSUB_SINGLESUBST_HH
+
+#include "Common.hh"
+#include "SingleSubstFormat1.hh"
+#include "SingleSubstFormat2.hh"
+
+namespace OT {
+namespace Layout {
+namespace GSUB_impl {
+
+struct SingleSubst
+{
+ protected:
+ union {
+ HBUINT16 format; /* Format identifier */
+ SingleSubstFormat1_3<SmallTypes> format1;
+ SingleSubstFormat2_4<SmallTypes> format2;
+#ifndef HB_NO_BEYOND_64K
+ SingleSubstFormat1_3<MediumTypes> format3;
+ SingleSubstFormat2_4<MediumTypes> format4;
+#endif
+ } u;
+
+ public:
+
+ template <typename context_t, typename ...Ts>
+ typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
+ {
+ if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value ();
+ TRACE_DISPATCH (this, u.format);
+ switch (u.format) {
+ case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
+ case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...));
+#ifndef HB_NO_BEYOND_64K
+ case 3: return_trace (c->dispatch (u.format3, std::forward<Ts> (ds)...));
+ case 4: return_trace (c->dispatch (u.format4, std::forward<Ts> (ds)...));
+#endif
+ default:return_trace (c->default_return_value ());
+ }
+ }
+
+ template<typename Iterator,
+ hb_requires (hb_is_sorted_source_of (Iterator,
+ const hb_codepoint_pair_t))>
+ bool serialize (hb_serialize_context_t *c,
+ Iterator glyphs)
+ {
+ TRACE_SERIALIZE (this);
+ if (unlikely (!c->extend_min (u.format))) return_trace (false);
+ unsigned format = 2;
+ unsigned delta = 0;
+ if (glyphs)
+ {
+ format = 1;
+ hb_codepoint_t mask = 0xFFFFu;
+
+#ifndef HB_NO_BEYOND_64K
+ if (+ glyphs
+ | hb_map_retains_sorting (hb_second)
+ | hb_filter ([] (hb_codepoint_t gid) { return gid > 0xFFFFu; }))
+ {
+ format += 2;
+ mask = 0xFFFFFFu;
+ }
+#endif
+
+ auto get_delta = [=] (hb_codepoint_pair_t _)
+ { return (unsigned) (_.second - _.first) & mask; };
+ delta = get_delta (*glyphs);
+ if (!hb_all (++(+glyphs), delta, get_delta)) format += 1;
+ }
+
+ u.format = format;
+ switch (u.format) {
+ case 1: return_trace (u.format1.serialize (c,
+ + glyphs
+ | hb_map_retains_sorting (hb_first),
+ delta));
+ case 2: return_trace (u.format2.serialize (c, glyphs));
+#ifndef HB_NO_BEYOND_64K
+ case 3: return_trace (u.format3.serialize (c,
+ + glyphs
+ | hb_map_retains_sorting (hb_first),
+ delta));
+ case 4: return_trace (u.format4.serialize (c, glyphs));
+#endif
+ default:return_trace (false);
+ }
+ }
+};
+
+template<typename Iterator>
+static void
+SingleSubst_serialize (hb_serialize_context_t *c,
+ Iterator it)
+{ c->start_embed<SingleSubst> ()->serialize (c, it); }
+
+}
+}
+}
+
+#endif /* OT_LAYOUT_GSUB_SINGLESUBST_HH */
diff --git a/gfx/harfbuzz/src/OT/Layout/GSUB/SingleSubstFormat1.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/SingleSubstFormat1.hh
new file mode 100644
index 0000000000..850be86c04
--- /dev/null
+++ b/gfx/harfbuzz/src/OT/Layout/GSUB/SingleSubstFormat1.hh
@@ -0,0 +1,204 @@
+#ifndef OT_LAYOUT_GSUB_SINGLESUBSTFORMAT1_HH
+#define OT_LAYOUT_GSUB_SINGLESUBSTFORMAT1_HH
+
+#include "Common.hh"
+
+namespace OT {
+namespace Layout {
+namespace GSUB_impl {
+
+template <typename Types>
+struct SingleSubstFormat1_3
+{
+ protected:
+ HBUINT16 format; /* Format identifier--format = 1 */
+ typename Types::template OffsetTo<Coverage>
+ coverage; /* Offset to Coverage table--from
+ * beginning of Substitution table */
+ typename Types::HBUINT
+ deltaGlyphID; /* Add to original GlyphID to get
+ * substitute GlyphID, modulo 0x10000 */
+
+ public:
+ DEFINE_SIZE_STATIC (2 + 2 * Types::size);
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) &&
+ coverage.sanitize (c, this) &&
+ /* The coverage table may use a range to represent a set
+ * of glyphs, which means a small number of bytes can
+ * generate a large glyph set. Manually modify the
+ * sanitizer max ops to take this into account.
+ *
+ * Note: This check *must* be right after coverage sanitize. */
+ c->check_ops ((this + coverage).get_population () >> 1));
+ }
+
+ hb_codepoint_t get_mask () const
+ { return (1 << (8 * Types::size)) - 1; }
+
+ bool intersects (const hb_set_t *glyphs) const
+ { return (this+coverage).intersects (glyphs); }
+
+ bool may_have_non_1to1 () const
+ { return false; }
+
+ void closure (hb_closure_context_t *c) const
+ {
+ hb_codepoint_t d = deltaGlyphID;
+ hb_codepoint_t mask = get_mask ();
+
+ /* Help fuzzer avoid this function as much. */
+ unsigned pop = (this+coverage).get_population ();
+ if (pop >= mask)
+ return;
+
+ hb_set_t intersection;
+ (this+coverage).intersect_set (c->parent_active_glyphs (), intersection);
+
+ /* In degenerate fuzzer-found fonts, but not real fonts,
+ * this table can keep adding new glyphs in each round of closure.
+ * Refuse to close-over, if it maps glyph range to overlapping range. */
+ hb_codepoint_t min_before = intersection.get_min ();
+ hb_codepoint_t max_before = intersection.get_max ();
+ hb_codepoint_t min_after = (min_before + d) & mask;
+ hb_codepoint_t max_after = (max_before + d) & mask;
+ if (intersection.get_population () == max_before - min_before + 1 &&
+ ((min_before <= min_after && min_after <= max_before) ||
+ (min_before <= max_after && max_after <= max_before)))
+ return;
+
+ + hb_iter (intersection)
+ | hb_map ([d, mask] (hb_codepoint_t g) { return (g + d) & mask; })
+ | hb_sink (c->output)
+ ;
+ }
+
+ void closure_lookups (hb_closure_lookups_context_t *c) const {}
+
+ void collect_glyphs (hb_collect_glyphs_context_t *c) const
+ {
+ if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
+ hb_codepoint_t d = deltaGlyphID;
+ hb_codepoint_t mask = get_mask ();
+
+ + hb_iter (this+coverage)
+ | hb_map ([d, mask] (hb_codepoint_t g) { return (g + d) & mask; })
+ | hb_sink (c->output)
+ ;
+ }
+
+ const Coverage &get_coverage () const { return this+coverage; }
+
+ bool would_apply (hb_would_apply_context_t *c) const
+ { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; }
+
+ unsigned
+ get_glyph_alternates (hb_codepoint_t glyph_id,
+ unsigned start_offset,
+ unsigned *alternate_count /* IN/OUT. May be NULL. */,
+ hb_codepoint_t *alternate_glyphs /* OUT. May be NULL. */) const
+ {
+ unsigned int index = (this+coverage).get_coverage (glyph_id);
+ if (likely (index == NOT_COVERED))
+ {
+ if (alternate_count)
+ *alternate_count = 0;
+ return 0;
+ }
+
+ if (alternate_count && *alternate_count)
+ {
+ hb_codepoint_t d = deltaGlyphID;
+ hb_codepoint_t mask = get_mask ();
+
+ glyph_id = (glyph_id + d) & mask;
+
+ *alternate_glyphs = glyph_id;
+ *alternate_count = 1;
+ }
+
+ return 1;
+ }
+
+ bool apply (hb_ot_apply_context_t *c) const
+ {
+ TRACE_APPLY (this);
+ hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
+ unsigned int index = (this+coverage).get_coverage (glyph_id);
+ if (likely (index == NOT_COVERED)) return_trace (false);
+
+ hb_codepoint_t d = deltaGlyphID;
+ hb_codepoint_t mask = get_mask ();
+
+ glyph_id = (glyph_id + d) & mask;
+
+ if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+ {
+ c->buffer->sync_so_far ();
+ c->buffer->message (c->font,
+ "replacing glyph at %u (single substitution)",
+ c->buffer->idx);
+ }
+
+ c->replace_glyph (glyph_id);
+
+ if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+ {
+ c->buffer->message (c->font,
+ "replaced glyph at %u (single substitution)",
+ c->buffer->idx - 1u);
+ }
+
+ return_trace (true);
+ }
+
+ template<typename Iterator,
+ hb_requires (hb_is_sorted_source_of (Iterator, hb_codepoint_t))>
+ bool serialize (hb_serialize_context_t *c,
+ Iterator glyphs,
+ unsigned delta)
+ {
+ TRACE_SERIALIZE (this);
+ if (unlikely (!c->extend_min (this))) return_trace (false);
+ if (unlikely (!coverage.serialize_serialize (c, glyphs))) return_trace (false);
+ c->check_assign (deltaGlyphID, delta, HB_SERIALIZE_ERROR_INT_OVERFLOW);
+ return_trace (true);
+ }
+
+ bool subset (hb_subset_context_t *c) const
+ {
+ TRACE_SUBSET (this);
+ const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
+ const hb_map_t &glyph_map = *c->plan->glyph_map;
+
+ hb_codepoint_t d = deltaGlyphID;
+ hb_codepoint_t mask = get_mask ();
+
+ hb_set_t intersection;
+ (this+coverage).intersect_set (glyphset, intersection);
+
+ auto it =
+ + hb_iter (intersection)
+ | hb_map_retains_sorting ([d, mask] (hb_codepoint_t g) {
+ return hb_codepoint_pair_t (g,
+ (g + d) & mask); })
+ | hb_filter (glyphset, hb_second)
+ | hb_map_retains_sorting ([&] (hb_codepoint_pair_t p) -> hb_codepoint_pair_t
+ { return hb_pair (glyph_map[p.first], glyph_map[p.second]); })
+ ;
+
+ bool ret = bool (it);
+ SingleSubst_serialize (c->serializer, it);
+ return_trace (ret);
+ }
+};
+
+}
+}
+}
+
+
+#endif /* OT_LAYOUT_GSUB_SINGLESUBSTFORMAT1_HH */
diff --git a/gfx/harfbuzz/src/OT/Layout/GSUB/SingleSubstFormat2.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/SingleSubstFormat2.hh
new file mode 100644
index 0000000000..9c651abe71
--- /dev/null
+++ b/gfx/harfbuzz/src/OT/Layout/GSUB/SingleSubstFormat2.hh
@@ -0,0 +1,176 @@
+#ifndef OT_LAYOUT_GSUB_SINGLESUBSTFORMAT2_HH
+#define OT_LAYOUT_GSUB_SINGLESUBSTFORMAT2_HH
+
+#include "Common.hh"
+
+namespace OT {
+namespace Layout {
+namespace GSUB_impl {
+
+template <typename Types>
+struct SingleSubstFormat2_4
+{
+ protected:
+ HBUINT16 format; /* Format identifier--format = 2 */
+ typename Types::template OffsetTo<Coverage>
+ coverage; /* Offset to Coverage table--from
+ * beginning of Substitution table */
+ Array16Of<typename Types::HBGlyphID>
+ substitute; /* Array of substitute
+ * GlyphIDs--ordered by Coverage Index */
+
+ public:
+ DEFINE_SIZE_ARRAY (4 + Types::size, substitute);
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (coverage.sanitize (c, this) && substitute.sanitize (c));
+ }
+
+ bool intersects (const hb_set_t *glyphs) const
+ { return (this+coverage).intersects (glyphs); }
+
+ bool may_have_non_1to1 () const
+ { return false; }
+
+ void closure (hb_closure_context_t *c) const
+ {
+ auto &cov = this+coverage;
+ auto &glyph_set = c->parent_active_glyphs ();
+
+ if (substitute.len > glyph_set.get_population () * 4)
+ {
+ for (auto g : glyph_set)
+ {
+ unsigned i = cov.get_coverage (g);
+ if (i == NOT_COVERED || i >= substitute.len)
+ continue;
+ c->output->add (substitute.arrayZ[i]);
+ }
+
+ return;
+ }
+
+ + hb_zip (cov, substitute)
+ | hb_filter (glyph_set, hb_first)
+ | hb_map (hb_second)
+ | hb_sink (c->output)
+ ;
+ }
+
+ void closure_lookups (hb_closure_lookups_context_t *c) const {}
+
+ void collect_glyphs (hb_collect_glyphs_context_t *c) const
+ {
+ if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
+ + hb_zip (this+coverage, substitute)
+ | hb_map (hb_second)
+ | hb_sink (c->output)
+ ;
+ }
+
+ const Coverage &get_coverage () const { return this+coverage; }
+
+ bool would_apply (hb_would_apply_context_t *c) const
+ { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; }
+
+ unsigned
+ get_glyph_alternates (hb_codepoint_t glyph_id,
+ unsigned start_offset,
+ unsigned *alternate_count /* IN/OUT. May be NULL. */,
+ hb_codepoint_t *alternate_glyphs /* OUT. May be NULL. */) const
+ {
+ unsigned int index = (this+coverage).get_coverage (glyph_id);
+ if (likely (index == NOT_COVERED))
+ {
+ if (alternate_count)
+ *alternate_count = 0;
+ return 0;
+ }
+
+ if (alternate_count && *alternate_count)
+ {
+ glyph_id = substitute[index];
+
+ *alternate_glyphs = glyph_id;
+ *alternate_count = 1;
+ }
+
+ return 1;
+ }
+
+ bool apply (hb_ot_apply_context_t *c) const
+ {
+ TRACE_APPLY (this);
+ unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
+ if (likely (index == NOT_COVERED)) return_trace (false);
+
+ if (unlikely (index >= substitute.len)) return_trace (false);
+
+ if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+ {
+ c->buffer->sync_so_far ();
+ c->buffer->message (c->font,
+ "replacing glyph at %u (single substitution)",
+ c->buffer->idx);
+ }
+
+ c->replace_glyph (substitute[index]);
+
+ if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
+ {
+ c->buffer->message (c->font,
+ "replaced glyph at %u (single substitution)",
+ c->buffer->idx - 1u);
+ }
+
+ return_trace (true);
+ }
+
+ template<typename Iterator,
+ hb_requires (hb_is_sorted_source_of (Iterator,
+ hb_codepoint_pair_t))>
+ bool serialize (hb_serialize_context_t *c,
+ Iterator it)
+ {
+ TRACE_SERIALIZE (this);
+ auto substitutes =
+ + it
+ | hb_map (hb_second)
+ ;
+ auto glyphs =
+ + it
+ | hb_map_retains_sorting (hb_first)
+ ;
+ if (unlikely (!c->extend_min (this))) return_trace (false);
+ if (unlikely (!substitute.serialize (c, substitutes))) return_trace (false);
+ if (unlikely (!coverage.serialize_serialize (c, glyphs))) return_trace (false);
+ return_trace (true);
+ }
+
+ bool subset (hb_subset_context_t *c) const
+ {
+ TRACE_SUBSET (this);
+ const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
+ const hb_map_t &glyph_map = *c->plan->glyph_map;
+
+ auto it =
+ + hb_zip (this+coverage, substitute)
+ | hb_filter (glyphset, hb_first)
+ | hb_filter (glyphset, hb_second)
+ | hb_map_retains_sorting ([&] (hb_pair_t<hb_codepoint_t, const typename Types::HBGlyphID &> p) -> hb_codepoint_pair_t
+ { return hb_pair (glyph_map[p.first], glyph_map[p.second]); })
+ ;
+
+ bool ret = bool (it);
+ SingleSubst_serialize (c->serializer, it);
+ return_trace (ret);
+ }
+};
+
+}
+}
+}
+
+#endif /* OT_LAYOUT_GSUB_SINGLESUBSTFORMAT2_HH */
diff --git a/gfx/harfbuzz/src/OT/Layout/GSUB/SubstLookup.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/SubstLookup.hh
new file mode 100644
index 0000000000..d49dcc0e0f
--- /dev/null
+++ b/gfx/harfbuzz/src/OT/Layout/GSUB/SubstLookup.hh
@@ -0,0 +1,220 @@
+#ifndef OT_LAYOUT_GSUB_SUBSTLOOKUP_HH
+#define OT_LAYOUT_GSUB_SUBSTLOOKUP_HH
+
+#include "Common.hh"
+#include "SubstLookupSubTable.hh"
+
+namespace OT {
+namespace Layout {
+namespace GSUB_impl {
+
+struct SubstLookup : Lookup
+{
+ using SubTable = SubstLookupSubTable;
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ { return Lookup::sanitize<SubTable> (c); }
+
+ const SubTable& get_subtable (unsigned int i) const
+ { return Lookup::get_subtable<SubTable> (i); }
+
+ static inline bool lookup_type_is_reverse (unsigned int lookup_type)
+ { return lookup_type == SubTable::ReverseChainSingle; }
+
+ bool is_reverse () const
+ {
+ unsigned int type = get_type ();
+ if (unlikely (type == SubTable::Extension))
+ return get_subtable (0).u.extension.is_reverse ();
+ return lookup_type_is_reverse (type);
+ }
+
+ bool may_have_non_1to1 () const
+ {
+ hb_have_non_1to1_context_t c;
+ return dispatch (&c);
+ }
+
+ bool apply (hb_ot_apply_context_t *c) const
+ {
+ TRACE_APPLY (this);
+ return_trace (dispatch (c));
+ }
+
+ bool intersects (const hb_set_t *glyphs) const
+ {
+ hb_intersects_context_t c (glyphs);
+ return dispatch (&c);
+ }
+
+ hb_closure_context_t::return_t closure (hb_closure_context_t *c, unsigned int this_index) const
+ {
+ if (!c->should_visit_lookup (this_index))
+ return hb_closure_context_t::default_return_value ();
+
+ c->set_recurse_func (dispatch_closure_recurse_func);
+
+ hb_closure_context_t::return_t ret = dispatch (c);
+
+ c->flush ();
+
+ return ret;
+ }
+
+ hb_closure_lookups_context_t::return_t closure_lookups (hb_closure_lookups_context_t *c, unsigned this_index) const
+ {
+ if (c->is_lookup_visited (this_index))
+ return hb_closure_lookups_context_t::default_return_value ();
+
+ c->set_lookup_visited (this_index);
+ if (!intersects (c->glyphs))
+ {
+ c->set_lookup_inactive (this_index);
+ return hb_closure_lookups_context_t::default_return_value ();
+ }
+
+ hb_closure_lookups_context_t::return_t ret = dispatch (c);
+ return ret;
+ }
+
+ hb_collect_glyphs_context_t::return_t collect_glyphs (hb_collect_glyphs_context_t *c) const
+ {
+ c->set_recurse_func (dispatch_recurse_func<hb_collect_glyphs_context_t>);
+ return dispatch (c);
+ }
+
+ template <typename set_t>
+ void collect_coverage (set_t *glyphs) const
+ {
+ hb_collect_coverage_context_t<set_t> c (glyphs);
+ dispatch (&c);
+ }
+
+ bool would_apply (hb_would_apply_context_t *c,
+ const hb_ot_layout_lookup_accelerator_t *accel) const
+ {
+ if (unlikely (!c->len)) return false;
+ if (!accel->may_have (c->glyphs[0])) return false;
+ return dispatch (c);
+ }
+
+ template<typename Glyphs, typename Substitutes,
+ hb_requires (hb_is_sorted_source_of (Glyphs,
+ const hb_codepoint_t) &&
+ hb_is_source_of (Substitutes,
+ const hb_codepoint_t))>
+ bool serialize_single (hb_serialize_context_t *c,
+ uint32_t lookup_props,
+ Glyphs glyphs,
+ Substitutes substitutes)
+ {
+ TRACE_SERIALIZE (this);
+ if (unlikely (!Lookup::serialize (c, SubTable::Single, lookup_props, 1))) return_trace (false);
+ if (c->push<SubTable> ()->u.single.serialize (c, hb_zip (glyphs, substitutes)))
+ {
+ c->add_link (get_subtables<SubTable> ()[0], c->pop_pack ());
+ return_trace (true);
+ }
+ c->pop_discard ();
+ return_trace (false);
+ }
+
+ template<typename Iterator,
+ hb_requires (hb_is_sorted_iterator (Iterator))>
+ bool serialize (hb_serialize_context_t *c,
+ uint32_t lookup_props,
+ Iterator it)
+ {
+ TRACE_SERIALIZE (this);
+ if (unlikely (!Lookup::serialize (c, SubTable::Multiple, lookup_props, 1))) return_trace (false);
+ if (c->push<SubTable> ()->u.multiple.
+ serialize (c, it))
+ {
+ c->add_link (get_subtables<SubTable> ()[0], c->pop_pack ());
+ return_trace (true);
+ }
+ c->pop_discard ();
+ return_trace (false);
+ }
+
+ bool serialize_alternate (hb_serialize_context_t *c,
+ uint32_t lookup_props,
+ hb_sorted_array_t<const HBGlyphID16> glyphs,
+ hb_array_t<const unsigned int> alternate_len_list,
+ hb_array_t<const HBGlyphID16> alternate_glyphs_list)
+ {
+ TRACE_SERIALIZE (this);
+ if (unlikely (!Lookup::serialize (c, SubTable::Alternate, lookup_props, 1))) return_trace (false);
+
+ if (c->push<SubTable> ()->u.alternate.
+ serialize (c,
+ glyphs,
+ alternate_len_list,
+ alternate_glyphs_list))
+ {
+ c->add_link (get_subtables<SubTable> ()[0], c->pop_pack ());
+ return_trace (true);
+ }
+ c->pop_discard ();
+ return_trace (false);
+ }
+
+ bool serialize_ligature (hb_serialize_context_t *c,
+ uint32_t lookup_props,
+ hb_sorted_array_t<const HBGlyphID16> first_glyphs,
+ hb_array_t<const unsigned int> ligature_per_first_glyph_count_list,
+ hb_array_t<const HBGlyphID16> ligatures_list,
+ hb_array_t<const unsigned int> component_count_list,
+ hb_array_t<const HBGlyphID16> component_list /* Starting from second for each ligature */)
+ {
+ TRACE_SERIALIZE (this);
+ if (unlikely (!Lookup::serialize (c, SubTable::Ligature, lookup_props, 1))) return_trace (false);
+ if (c->push<SubTable> ()->u.ligature.
+ serialize (c,
+ first_glyphs,
+ ligature_per_first_glyph_count_list,
+ ligatures_list,
+ component_count_list,
+ component_list))
+ {
+ c->add_link (get_subtables<SubTable> ()[0], c->pop_pack ());
+ return_trace (true);
+ }
+ c->pop_discard ();
+ return_trace (false);
+ }
+
+ template <typename context_t>
+ static inline typename context_t::return_t dispatch_recurse_func (context_t *c, unsigned int lookup_index);
+
+ static inline typename hb_closure_context_t::return_t closure_glyphs_recurse_func (hb_closure_context_t *c, unsigned lookup_index, hb_set_t *covered_seq_indices, unsigned seq_index, unsigned end_index);
+
+ static inline hb_closure_context_t::return_t dispatch_closure_recurse_func (hb_closure_context_t *c, unsigned lookup_index, hb_set_t *covered_seq_indices, unsigned seq_index, unsigned end_index)
+ {
+ if (!c->should_visit_lookup (lookup_index))
+ return hb_empty_t ();
+
+ hb_closure_context_t::return_t ret = closure_glyphs_recurse_func (c, lookup_index, covered_seq_indices, seq_index, end_index);
+
+ /* While in theory we should flush here, it will cause timeouts because a recursive
+ * lookup can keep growing the glyph set. Skip, and outer loop will retry up to
+ * HB_CLOSURE_MAX_STAGES time, which should be enough for every realistic font. */
+ //c->flush ();
+
+ return ret;
+ }
+
+ template <typename context_t, typename ...Ts>
+ typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
+ { return Lookup::dispatch<SubTable> (c, std::forward<Ts> (ds)...); }
+
+ bool subset (hb_subset_context_t *c) const
+ { return Lookup::subset<SubTable> (c); }
+};
+
+
+}
+}
+}
+
+#endif /* OT_LAYOUT_GSUB_SUBSTLOOKUP_HH */
diff --git a/gfx/harfbuzz/src/OT/Layout/GSUB/SubstLookupSubTable.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/SubstLookupSubTable.hh
new file mode 100644
index 0000000000..a525fba039
--- /dev/null
+++ b/gfx/harfbuzz/src/OT/Layout/GSUB/SubstLookupSubTable.hh
@@ -0,0 +1,77 @@
+#ifndef OT_LAYOUT_GSUB_SUBSTLOOKUPSUBTABLE_HH
+#define OT_LAYOUT_GSUB_SUBSTLOOKUPSUBTABLE_HH
+
+#include "Common.hh"
+#include "SingleSubst.hh"
+#include "MultipleSubst.hh"
+#include "AlternateSubst.hh"
+#include "LigatureSubst.hh"
+#include "ContextSubst.hh"
+#include "ChainContextSubst.hh"
+#include "ExtensionSubst.hh"
+#include "ReverseChainSingleSubst.hh"
+
+namespace OT {
+namespace Layout {
+namespace GSUB_impl {
+
+struct SubstLookupSubTable
+{
+ friend struct ::OT::Lookup;
+ friend struct SubstLookup;
+
+ protected:
+ union {
+ SingleSubst single;
+ MultipleSubst multiple;
+ AlternateSubst alternate;
+ LigatureSubst ligature;
+ ContextSubst context;
+ ChainContextSubst chainContext;
+ ExtensionSubst extension;
+ ReverseChainSingleSubst reverseChainContextSingle;
+ } u;
+ public:
+ DEFINE_SIZE_MIN (0);
+
+ enum Type {
+ Single = 1,
+ Multiple = 2,
+ Alternate = 3,
+ Ligature = 4,
+ Context = 5,
+ ChainContext = 6,
+ Extension = 7,
+ ReverseChainSingle = 8
+ };
+
+ template <typename context_t, typename ...Ts>
+ typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type, Ts&&... ds) const
+ {
+ TRACE_DISPATCH (this, lookup_type);
+ switch (lookup_type) {
+ case Single: return_trace (u.single.dispatch (c, std::forward<Ts> (ds)...));
+ case Multiple: return_trace (u.multiple.dispatch (c, std::forward<Ts> (ds)...));
+ case Alternate: return_trace (u.alternate.dispatch (c, std::forward<Ts> (ds)...));
+ case Ligature: return_trace (u.ligature.dispatch (c, std::forward<Ts> (ds)...));
+ case Context: return_trace (u.context.dispatch (c, std::forward<Ts> (ds)...));
+ case ChainContext: return_trace (u.chainContext.dispatch (c, std::forward<Ts> (ds)...));
+ case Extension: return_trace (u.extension.dispatch (c, std::forward<Ts> (ds)...));
+ case ReverseChainSingle: return_trace (u.reverseChainContextSingle.dispatch (c, std::forward<Ts> (ds)...));
+ default: return_trace (c->default_return_value ());
+ }
+ }
+
+ bool intersects (const hb_set_t *glyphs, unsigned int lookup_type) const
+ {
+ hb_intersects_context_t c (glyphs);
+ return dispatch (&c, lookup_type);
+ }
+};
+
+
+}
+}
+}
+
+#endif /* HB_OT_LAYOUT_GSUB_SUBSTLOOKUPSUBTABLE_HH */