浏览代码

Merge pull request #103491 from bruvzg/hb1040

Update HarfBuzz to 10.4.0
Thaddeus Crews 6 月之前
父节点
当前提交
3f759f488d
共有 100 个文件被更改,包括 4971 次插入2457 次删除
  1. 1 1
      modules/text_server_adv/SCsub
  2. 1 1
      modules/text_server_adv/gdextension_build/SConstruct
  3. 1 1
      thirdparty/README.md
  4. 3 1
      thirdparty/harfbuzz/src/OT/Color/CBDT/CBDT.hh
  5. 37 32
      thirdparty/harfbuzz/src/OT/Color/COLR/COLR.hh
  6. 8 0
      thirdparty/harfbuzz/src/OT/Color/CPAL/CPAL.hh
  7. 22 0
      thirdparty/harfbuzz/src/OT/Layout/Common/Coverage.hh
  8. 2 0
      thirdparty/harfbuzz/src/OT/Layout/Common/CoverageFormat1.hh
  9. 2 0
      thirdparty/harfbuzz/src/OT/Layout/Common/CoverageFormat2.hh
  10. 41 3
      thirdparty/harfbuzz/src/OT/Layout/GPOS/PairPosFormat1.hh
  11. 56 2
      thirdparty/harfbuzz/src/OT/Layout/GPOS/PairPosFormat2.hh
  12. 1 1
      thirdparty/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat1.hh
  13. 1 1
      thirdparty/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat2.hh
  14. 1 1
      thirdparty/harfbuzz/src/OT/Layout/GSUB/AlternateSubstFormat1.hh
  15. 41 4
      thirdparty/harfbuzz/src/OT/Layout/GSUB/LigatureSubstFormat1.hh
  16. 1 1
      thirdparty/harfbuzz/src/OT/Layout/GSUB/MultipleSubstFormat1.hh
  17. 1 1
      thirdparty/harfbuzz/src/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh
  18. 1 1
      thirdparty/harfbuzz/src/OT/Layout/GSUB/SingleSubstFormat1.hh
  19. 1 1
      thirdparty/harfbuzz/src/OT/Layout/GSUB/SingleSubstFormat2.hh
  20. 3 0
      thirdparty/harfbuzz/src/OT/Layout/types.hh
  21. 81 23
      thirdparty/harfbuzz/src/OT/Var/VARC/VARC.cc
  22. 95 70
      thirdparty/harfbuzz/src/OT/Var/VARC/VARC.hh
  23. 33 7
      thirdparty/harfbuzz/src/OT/Var/VARC/coord-setter.hh
  24. 1 1
      thirdparty/harfbuzz/src/OT/glyf/CompositeGlyph.hh
  25. 42 24
      thirdparty/harfbuzz/src/OT/glyf/Glyph.hh
  26. 8 10
      thirdparty/harfbuzz/src/OT/glyf/SimpleGlyph.hh
  27. 133 30
      thirdparty/harfbuzz/src/OT/glyf/glyf.hh
  28. 51 49
      thirdparty/harfbuzz/src/OT/glyf/path-builder.hh
  29. 5 5
      thirdparty/harfbuzz/src/OT/name/name.hh
  30. 178 65
      thirdparty/harfbuzz/src/hb-aat-layout-common.hh
  31. 54 70
      thirdparty/harfbuzz/src/hb-aat-layout-kerx-table.hh
  32. 240 155
      thirdparty/harfbuzz/src/hb-aat-layout-morx-table.hh
  33. 93 91
      thirdparty/harfbuzz/src/hb-aat-layout-trak-table.hh
  34. 34 12
      thirdparty/harfbuzz/src/hb-aat-layout.cc
  35. 3 5
      thirdparty/harfbuzz/src/hb-aat-layout.hh
  36. 8 7
      thirdparty/harfbuzz/src/hb-aat-map.cc
  37. 5 5
      thirdparty/harfbuzz/src/hb-algs.hh
  38. 2 1
      thirdparty/harfbuzz/src/hb-array.hh
  39. 1 0
      thirdparty/harfbuzz/src/hb-atomic.hh
  40. 41 14
      thirdparty/harfbuzz/src/hb-bit-page.hh
  41. 4 0
      thirdparty/harfbuzz/src/hb-bit-set-invertible.hh
  42. 46 19
      thirdparty/harfbuzz/src/hb-bit-set.hh
  43. 222 212
      thirdparty/harfbuzz/src/hb-buffer-deserialize-json.hh
  44. 4 4
      thirdparty/harfbuzz/src/hb-buffer.cc
  45. 9 8
      thirdparty/harfbuzz/src/hb-buffer.hh
  46. 9 3
      thirdparty/harfbuzz/src/hb-cairo.cc
  47. 1 1
      thirdparty/harfbuzz/src/hb-cff-interp-common.hh
  48. 3 0
      thirdparty/harfbuzz/src/hb-common.cc
  49. 1 2
      thirdparty/harfbuzz/src/hb-config.hh
  50. 93 15
      thirdparty/harfbuzz/src/hb-coretext-font.cc
  51. 38 10
      thirdparty/harfbuzz/src/hb-coretext-shape.cc
  52. 164 0
      thirdparty/harfbuzz/src/hb-decycler.hh
  53. 107 8
      thirdparty/harfbuzz/src/hb-directwrite.cc
  54. 15 1
      thirdparty/harfbuzz/src/hb-directwrite.h
  55. 44 5
      thirdparty/harfbuzz/src/hb-face.cc
  56. 7 2
      thirdparty/harfbuzz/src/hb-face.h
  57. 15 17
      thirdparty/harfbuzz/src/hb-ft-colr.hh
  58. 78 7
      thirdparty/harfbuzz/src/hb-ft.cc
  59. 8 1
      thirdparty/harfbuzz/src/hb-ft.h
  60. 9 0
      thirdparty/harfbuzz/src/hb-geometry.hh
  61. 1 1
      thirdparty/harfbuzz/src/hb-null.hh
  62. 171 23
      thirdparty/harfbuzz/src/hb-open-type.hh
  63. 0 9
      thirdparty/harfbuzz/src/hb-ot-cff1-table.cc
  64. 0 1
      thirdparty/harfbuzz/src/hb-ot-cff1-table.hh
  65. 0 9
      thirdparty/harfbuzz/src/hb-ot-cff2-table.cc
  66. 0 1
      thirdparty/harfbuzz/src/hb-ot-cff2-table.hh
  67. 4 0
      thirdparty/harfbuzz/src/hb-ot-cmap-table.hh
  68. 4 1
      thirdparty/harfbuzz/src/hb-ot-face-table-list.hh
  69. 1 0
      thirdparty/harfbuzz/src/hb-ot-face.cc
  70. 50 10
      thirdparty/harfbuzz/src/hb-ot-font.cc
  71. 1 1
      thirdparty/harfbuzz/src/hb-ot-hdmx-table.hh
  72. 1 1
      thirdparty/harfbuzz/src/hb-ot-hmtx-table.hh
  73. 2 2
      thirdparty/harfbuzz/src/hb-ot-kern-table.hh
  74. 1 1
      thirdparty/harfbuzz/src/hb-ot-layout-base-table.hh
  75. 38 25
      thirdparty/harfbuzz/src/hb-ot-layout-common.hh
  76. 121 52
      thirdparty/harfbuzz/src/hb-ot-layout-gsubgpos.hh
  77. 22 8
      thirdparty/harfbuzz/src/hb-ot-layout.cc
  78. 7 3
      thirdparty/harfbuzz/src/hb-ot-layout.hh
  79. 14 0
      thirdparty/harfbuzz/src/hb-ot-map.cc
  80. 3 0
      thirdparty/harfbuzz/src/hb-ot-map.hh
  81. 2 2
      thirdparty/harfbuzz/src/hb-ot-os2-table.hh
  82. 32 19
      thirdparty/harfbuzz/src/hb-ot-shape.cc
  83. 6 0
      thirdparty/harfbuzz/src/hb-ot-shape.h
  84. 2 13
      thirdparty/harfbuzz/src/hb-ot-shape.hh
  85. 2 0
      thirdparty/harfbuzz/src/hb-ot-shaper-arabic-fallback.hh
  86. 896 230
      thirdparty/harfbuzz/src/hb-ot-shaper-indic-machine.hh
  87. 7 4
      thirdparty/harfbuzz/src/hb-ot-shaper-indic-table.cc
  88. 386 256
      thirdparty/harfbuzz/src/hb-ot-shaper-myanmar-machine.hh
  89. 550 550
      thirdparty/harfbuzz/src/hb-ot-shaper-use-machine.hh
  90. 1 1
      thirdparty/harfbuzz/src/hb-ot-shaper-vowel-constraints.cc
  91. 210 3
      thirdparty/harfbuzz/src/hb-ot-tag-table.hh
  92. 41 23
      thirdparty/harfbuzz/src/hb-ot-var-common.hh
  93. 13 13
      thirdparty/harfbuzz/src/hb-ot-var-cvar-table.hh
  94. 90 52
      thirdparty/harfbuzz/src/hb-ot-var-gvar-table.hh
  95. 1 1
      thirdparty/harfbuzz/src/hb-paint.h
  96. 2 2
      thirdparty/harfbuzz/src/hb-sanitize.hh
  97. 12 15
      thirdparty/harfbuzz/src/hb-serialize.hh
  98. 58 109
      thirdparty/harfbuzz/src/hb-set-digest.hh
  99. 4 0
      thirdparty/harfbuzz/src/hb-set.hh
  100. 5 5
      thirdparty/harfbuzz/src/hb-shape-plan.cc

+ 1 - 1
modules/text_server_adv/SCsub

@@ -122,7 +122,7 @@ if env["builtin_harfbuzz"]:
         "src/hb-subset-instancer-iup.cc",
         "src/hb-subset-instancer-iup.cc",
         "src/hb-subset-instancer-solver.cc",
         "src/hb-subset-instancer-solver.cc",
         "src/hb-subset-plan.cc",
         "src/hb-subset-plan.cc",
-        "src/hb-subset-repacker.cc",
+        "src/hb-subset-serialize.cc",
         "src/hb-subset.cc",
         "src/hb-subset.cc",
         "src/hb-ucd.cc",
         "src/hb-ucd.cc",
         "src/hb-unicode.cc",
         "src/hb-unicode.cc",

+ 1 - 1
modules/text_server_adv/gdextension_build/SConstruct

@@ -362,7 +362,7 @@ thirdparty_harfbuzz_sources = [
     "src/hb-subset-instancer-iup.cc",
     "src/hb-subset-instancer-iup.cc",
     "src/hb-subset-instancer-solver.cc",
     "src/hb-subset-instancer-solver.cc",
     "src/hb-subset-plan.cc",
     "src/hb-subset-plan.cc",
-    "src/hb-subset-repacker.cc",
+    "src/hb-subset-serialize.cc",
     "src/hb-subset.cc",
     "src/hb-subset.cc",
     "src/hb-ucd.cc",
     "src/hb-ucd.cc",
     "src/hb-unicode.cc",
     "src/hb-unicode.cc",

+ 1 - 1
thirdparty/README.md

@@ -406,7 +406,7 @@ Files extracted from upstream source:
 ## harfbuzz
 ## harfbuzz
 
 
 - Upstream: https://github.com/harfbuzz/harfbuzz
 - Upstream: https://github.com/harfbuzz/harfbuzz
-- Version: 10.1.0 (9ef44a2d67ac870c1f7f671f6dc98d08a2579865, 2024)
+- Version: 10.4.0 (3ef8709829a5884517ad91a97b32b9435b2f20d1, 2025)
 - License: MIT
 - License: MIT
 
 
 Files extracted from upstream source:
 Files extracted from upstream source:

+ 3 - 1
thirdparty/harfbuzz/src/OT/Color/CBDT/CBDT.hh

@@ -941,10 +941,12 @@ struct CBDT
       }
       }
     }
     }
 
 
-    bool has_data () const { return cbdt.get_length (); }
+    bool has_data () const { return cbdt->version.major; }
 
 
     bool paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data) const
     bool paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data) const
     {
     {
+      if (!has_data ()) return false;
+
       hb_glyph_extents_t extents;
       hb_glyph_extents_t extents;
       hb_glyph_extents_t pixel_extents;
       hb_glyph_extents_t pixel_extents;
       hb_blob_t *blob = reference_png (font, glyph);
       hb_blob_t *blob = reference_png (font, glyph);

+ 37 - 32
thirdparty/harfbuzz/src/OT/Color/COLR/COLR.hh

@@ -29,11 +29,14 @@
 #define OT_COLOR_COLR_COLR_HH
 #define OT_COLOR_COLR_COLR_HH
 
 
 #include "../../../hb.hh"
 #include "../../../hb.hh"
+#include "../../../hb-decycler.hh"
 #include "../../../hb-open-type.hh"
 #include "../../../hb-open-type.hh"
 #include "../../../hb-ot-var-common.hh"
 #include "../../../hb-ot-var-common.hh"
 #include "../../../hb-paint.hh"
 #include "../../../hb-paint.hh"
 #include "../../../hb-paint-extents.hh"
 #include "../../../hb-paint-extents.hh"
 
 
+#include "../CPAL/CPAL.hh"
+
 /*
 /*
  * COLR -- Color
  * COLR -- Color
  * https://docs.microsoft.com/en-us/typography/opentype/spec/colr
  * https://docs.microsoft.com/en-us/typography/opentype/spec/colr
@@ -66,11 +69,11 @@ public:
   hb_paint_funcs_t *funcs;
   hb_paint_funcs_t *funcs;
   void *data;
   void *data;
   hb_font_t *font;
   hb_font_t *font;
-  unsigned int palette_index;
+  hb_array_t<const BGRAColor> palette;
   hb_color_t foreground;
   hb_color_t foreground;
   ItemVarStoreInstancer &instancer;
   ItemVarStoreInstancer &instancer;
-  hb_map_t current_glyphs;
-  hb_map_t current_layers;
+  hb_decycler_t glyphs_decycler;
+  hb_decycler_t layers_decycler;
   int depth_left = HB_MAX_NESTING_LEVEL;
   int depth_left = HB_MAX_NESTING_LEVEL;
   int edge_count = HB_MAX_GRAPH_EDGE_COUNT;
   int edge_count = HB_MAX_GRAPH_EDGE_COUNT;
 
 
@@ -85,7 +88,11 @@ public:
     funcs (funcs_),
     funcs (funcs_),
     data (data_),
     data (data_),
     font (font_),
     font (font_),
-    palette_index (palette_),
+    palette (
+#ifndef HB_NO_COLOR
+	     font->face->table.CPAL->get_palette_colors (palette_)
+#endif
+    ),
     foreground (foreground_),
     foreground (foreground_),
     instancer (instancer_)
     instancer (instancer_)
   { }
   { }
@@ -99,12 +106,7 @@ public:
     if (color_index != 0xffff)
     if (color_index != 0xffff)
     {
     {
       if (!funcs->custom_palette_color (data, color_index, &color))
       if (!funcs->custom_palette_color (data, color_index, &color))
-      {
-	unsigned int clen = 1;
-	hb_face_t *face = hb_font_get_face (font);
-
-	hb_ot_color_palette_get_colors (face, palette_index, color_index, &clen, &color);
-      }
+        color = palette[color_index];
 
 
       *is_foreground = false;
       *is_foreground = false;
     }
     }
@@ -1003,7 +1005,7 @@ struct PaintTransform
   void paint_glyph (hb_paint_context_t *c) const
   void paint_glyph (hb_paint_context_t *c) const
   {
   {
     TRACE_PAINT (this);
     TRACE_PAINT (this);
-    (this+transform).paint_glyph (c);
+    (this+transform).paint_glyph (c); // This does a push_transform()
     c->recurse (this+src);
     c->recurse (this+src);
     c->funcs->pop_transform (c->data);
     c->funcs->pop_transform (c->data);
   }
   }
@@ -2134,12 +2136,16 @@ struct COLR
 
 
     const ItemVariationStore &get_var_store () const
     const ItemVariationStore &get_var_store () const
     { return colr->get_var_store (); }
     { return colr->get_var_store (); }
+    const ItemVariationStore *get_var_store_ptr () const
+    { return colr->get_var_store_ptr (); }
 
 
     bool has_delta_set_index_map () const
     bool has_delta_set_index_map () const
     { return colr->has_delta_set_index_map (); }
     { return colr->has_delta_set_index_map (); }
 
 
     const DeltaSetIndexMap &get_delta_set_index_map () const
     const DeltaSetIndexMap &get_delta_set_index_map () const
     { return colr->get_delta_set_index_map (); }
     { return colr->get_delta_set_index_map (); }
+    const DeltaSetIndexMap *get_delta_set_index_map_ptr () const
+    { return colr->get_delta_set_index_map_ptr (); }
 
 
     private:
     private:
     hb_blob_ptr_t<COLR> colr;
     hb_blob_ptr_t<COLR> colr;
@@ -2232,9 +2238,13 @@ struct COLR
 
 
   const DeltaSetIndexMap &get_delta_set_index_map () const
   const DeltaSetIndexMap &get_delta_set_index_map () const
   { return has_delta_set_index_map () && hb_barrier () ? this+varIdxMap : Null (DeltaSetIndexMap); }
   { return has_delta_set_index_map () && hb_barrier () ? this+varIdxMap : Null (DeltaSetIndexMap); }
+  const DeltaSetIndexMap *get_delta_set_index_map_ptr () const
+  { return has_delta_set_index_map () && hb_barrier () ? &(this+varIdxMap) : nullptr; }
 
 
   const ItemVariationStore &get_var_store () const
   const ItemVariationStore &get_var_store () const
   { return has_var_store () && hb_barrier () ? this+varStore : Null (ItemVariationStore); }
   { return has_var_store () && hb_barrier () ? this+varStore : Null (ItemVariationStore); }
+  const ItemVariationStore *get_var_store_ptr () const
+  { return has_var_store () && hb_barrier () ? &(this+varStore) : nullptr; }
 
 
   const ClipList &get_clip_list () const
   const ClipList &get_clip_list () const
   { return has_clip_list () && hb_barrier () ? this+clipList : Null (ClipList); }
   { return has_clip_list () && hb_barrier () ? this+clipList : Null (ClipList); }
@@ -2482,9 +2492,9 @@ struct COLR
      * after instancing */
      * after instancing */
     if (!subset_varstore (c, colr_prime)) return_trace (false);
     if (!subset_varstore (c, colr_prime)) return_trace (false);
 
 
-    ItemVarStoreInstancer instancer (&(get_var_store ()),
-	                         &(get_delta_set_index_map ()),
-	                         c->plan->normalized_coords.as_array ());
+    ItemVarStoreInstancer instancer (get_var_store_ptr (),
+				     get_delta_set_index_map_ptr (),
+				     c->plan->normalized_coords.as_array ());
 
 
     if (!colr_prime->baseGlyphList.serialize_subset (c, baseGlyphList, this, instancer))
     if (!colr_prime->baseGlyphList.serialize_subset (c, baseGlyphList, this, instancer))
       return_trace (false);
       return_trace (false);
@@ -2513,8 +2523,8 @@ struct COLR
   get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const
   get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const
   {
   {
 
 
-    ItemVarStoreInstancer instancer (&(get_var_store ()),
-                                     &(get_delta_set_index_map ()),
+    ItemVarStoreInstancer instancer (get_var_store_ptr (),
+                                     get_delta_set_index_map_ptr (),
                                      hb_array (font->coords, font->num_coords));
                                      hb_array (font->coords, font->num_coords));
 
 
     if (get_clip (glyph, extents, instancer))
     if (get_clip (glyph, extents, instancer))
@@ -2575,11 +2585,13 @@ struct COLR
   bool
   bool
   paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, unsigned int palette_index, hb_color_t foreground, bool clip = true) const
   paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, unsigned int palette_index, hb_color_t foreground, bool clip = true) const
   {
   {
-    ItemVarStoreInstancer instancer (&(get_var_store ()),
-	                         &(get_delta_set_index_map ()),
-	                         hb_array (font->coords, font->num_coords));
+    ItemVarStoreInstancer instancer (get_var_store_ptr (),
+				     get_delta_set_index_map_ptr (),
+				     hb_array (font->coords, font->num_coords));
     hb_paint_context_t c (this, funcs, data, font, palette_index, foreground, instancer);
     hb_paint_context_t c (this, funcs, data, font, palette_index, foreground, instancer);
-    c.current_glyphs.add (glyph);
+
+    hb_decycler_node_t node (c.glyphs_decycler);
+    node.visit (glyph);
 
 
     if (version >= 1)
     if (version >= 1)
     {
     {
@@ -2695,19 +2707,16 @@ void PaintColrLayers::paint_glyph (hb_paint_context_t *c) const
 {
 {
   TRACE_PAINT (this);
   TRACE_PAINT (this);
   const LayerList &paint_offset_lists = c->get_colr_table ()->get_layerList ();
   const LayerList &paint_offset_lists = c->get_colr_table ()->get_layerList ();
+  hb_decycler_node_t node (c->layers_decycler);
   for (unsigned i = firstLayerIndex; i < firstLayerIndex + numLayers; i++)
   for (unsigned i = firstLayerIndex; i < firstLayerIndex + numLayers; i++)
   {
   {
-    if (unlikely (c->current_layers.has (i)))
-      continue;
-
-    c->current_layers.add (i);
+    if (unlikely (!node.visit (i)))
+      return;
 
 
     const Paint &paint = paint_offset_lists.get_paint (i);
     const Paint &paint = paint_offset_lists.get_paint (i);
     c->funcs->push_group (c->data);
     c->funcs->push_group (c->data);
     c->recurse (paint);
     c->recurse (paint);
     c->funcs->pop_group (c->data, HB_PAINT_COMPOSITE_MODE_SRC_OVER);
     c->funcs->pop_group (c->data, HB_PAINT_COMPOSITE_MODE_SRC_OVER);
-
-    c->current_layers.del (i);
   }
   }
 }
 }
 
 
@@ -2715,16 +2724,14 @@ void PaintColrGlyph::paint_glyph (hb_paint_context_t *c) const
 {
 {
   TRACE_PAINT (this);
   TRACE_PAINT (this);
 
 
-  if (unlikely (c->current_glyphs.has (gid)))
+  hb_decycler_node_t node (c->glyphs_decycler);
+  if (unlikely (!node.visit (gid)))
     return;
     return;
 
 
-  c->current_glyphs.add (gid);
-
   c->funcs->push_inverse_root_transform (c->data, c->font);
   c->funcs->push_inverse_root_transform (c->data, c->font);
   if (c->funcs->color_glyph (c->data, gid, c->font))
   if (c->funcs->color_glyph (c->data, gid, c->font))
   {
   {
     c->funcs->pop_transform (c->data);
     c->funcs->pop_transform (c->data);
-    c->current_glyphs.del (gid);
     return;
     return;
   }
   }
   c->funcs->pop_transform (c->data);
   c->funcs->pop_transform (c->data);
@@ -2747,8 +2754,6 @@ void PaintColrGlyph::paint_glyph (hb_paint_context_t *c) const
 
 
   if (has_clip_box)
   if (has_clip_box)
     c->funcs->pop_clip (c->data);
     c->funcs->pop_clip (c->data);
-
-  c->current_glyphs.del (gid);
 }
 }
 
 
 } /* namespace OT */
 } /* namespace OT */

+ 8 - 0
thirdparty/harfbuzz/src/OT/Color/CPAL/CPAL.hh

@@ -187,6 +187,14 @@ struct CPAL
   hb_ot_name_id_t get_color_name_id (unsigned int color_index) const
   hb_ot_name_id_t get_color_name_id (unsigned int color_index) const
   { return v1 ().get_color_name_id (this, color_index, numColors); }
   { return v1 ().get_color_name_id (this, color_index, numColors); }
 
 
+  hb_array_t<const BGRAColor> get_palette_colors (unsigned int palette_index) const
+  {
+    if (unlikely (palette_index >= numPalettes))
+      return hb_array_t<const BGRAColor> ();
+    unsigned int start_index = colorRecordIndicesZ[palette_index];
+    hb_array_t<const BGRAColor> all_colors ((this+colorRecordsZ).arrayZ, numColorRecords);
+    return all_colors.sub_array (start_index, numColors);
+  }
   unsigned int get_palette_colors (unsigned int  palette_index,
   unsigned int get_palette_colors (unsigned int  palette_index,
 				   unsigned int  start_offset,
 				   unsigned int  start_offset,
 				   unsigned int *color_count, /* IN/OUT.  May be NULL. */
 				   unsigned int *color_count, /* IN/OUT.  May be NULL. */

+ 22 - 0
thirdparty/harfbuzz/src/OT/Layout/Common/Coverage.hh

@@ -96,6 +96,15 @@ struct Coverage
     default:return NOT_COVERED;
     default:return NOT_COVERED;
     }
     }
   }
   }
+  unsigned int get_coverage (hb_codepoint_t glyph_id,
+			     hb_ot_lookup_cache_t *cache) const
+  {
+    unsigned coverage;
+    if (cache && cache->get (glyph_id, &coverage)) return coverage;
+    coverage = get_coverage (glyph_id);
+    if (cache) cache->set (glyph_id, coverage);
+    return coverage;
+  }
 
 
   unsigned get_population () const
   unsigned get_population () const
   {
   {
@@ -201,6 +210,19 @@ struct Coverage
     }
     }
   }
   }
 
 
+  unsigned cost () const
+  {
+    switch (u.format) {
+    case 1: hb_barrier (); return u.format1.cost ();
+    case 2: hb_barrier (); return u.format2.cost ();
+#ifndef HB_NO_BEYOND_64K
+    case 3: hb_barrier (); return u.format3.cost ();
+    case 4: hb_barrier (); return u.format4.cost ();
+#endif
+    default:return 0u;
+    }
+  }
+
   /* Might return false if array looks unsorted.
   /* Might return false if array looks unsorted.
    * Used for faster rejection of corrupt data. */
    * Used for faster rejection of corrupt data. */
   template <typename set_t>
   template <typename set_t>

+ 2 - 0
thirdparty/harfbuzz/src/OT/Layout/Common/CoverageFormat1.hh

@@ -103,6 +103,8 @@ struct CoverageFormat1_3
         intersect_glyphs << glyphArray[i];
         intersect_glyphs << glyphArray[i];
   }
   }
 
 
+  unsigned cost () const { return hb_bit_storage ((unsigned) glyphArray.len); /* bsearch cost */ }
+
   template <typename set_t>
   template <typename set_t>
   bool collect_coverage (set_t *glyphs) const
   bool collect_coverage (set_t *glyphs) const
   { return glyphs->add_sorted_array (glyphArray.as_array ()); }
   { return glyphs->add_sorted_array (glyphArray.as_array ()); }

+ 2 - 0
thirdparty/harfbuzz/src/OT/Layout/Common/CoverageFormat2.hh

@@ -157,6 +157,8 @@ struct CoverageFormat2_4
     }
     }
   }
   }
 
 
+  unsigned cost () const { return hb_bit_storage ((unsigned) rangeRecord.len); /* bsearch cost */ }
+
   template <typename set_t>
   template <typename set_t>
   bool collect_coverage (set_t *glyphs) const
   bool collect_coverage (set_t *glyphs) const
   {
   {

+ 41 - 3
thirdparty/harfbuzz/src/OT/Layout/GPOS/PairPosFormat1.hh

@@ -103,12 +103,50 @@ struct PairPosFormat1_3
 
 
   const Coverage &get_coverage () const { return this+coverage; }
   const Coverage &get_coverage () const { return this+coverage; }
 
 
-  bool apply (hb_ot_apply_context_t *c) const
+  unsigned cache_cost () const
+  {
+    return (this+coverage).cost ();
+  }
+  static void * cache_func (void *p, hb_ot_lookup_cache_op_t op)
+  {
+    switch (op)
+    {
+      case hb_ot_lookup_cache_op_t::CREATE:
+      {
+	hb_ot_lookup_cache_t *cache = (hb_ot_lookup_cache_t *) hb_malloc (sizeof (hb_ot_lookup_cache_t));
+	if (likely (cache))
+	  cache->clear ();
+	return cache;
+      }
+      case hb_ot_lookup_cache_op_t::ENTER:
+	return (void *) true;
+      case hb_ot_lookup_cache_op_t::LEAVE:
+	return nullptr;
+      case hb_ot_lookup_cache_op_t::DESTROY:
+      {
+	hb_ot_lookup_cache_t *cache = (hb_ot_lookup_cache_t *) p;
+	hb_free (cache);
+	return nullptr;
+      }
+    }
+    return nullptr;
+  }
+
+  bool apply_cached (hb_ot_apply_context_t *c) const { return _apply (c, true); }
+  bool apply (hb_ot_apply_context_t *c) const { return _apply (c, false); }
+  bool _apply (hb_ot_apply_context_t *c, bool cached) const
   {
   {
     TRACE_APPLY (this);
     TRACE_APPLY (this);
+
     hb_buffer_t *buffer = c->buffer;
     hb_buffer_t *buffer = c->buffer;
+
+#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
+    hb_ot_lookup_cache_t *cache = cached ? (hb_ot_lookup_cache_t *) c->lookup_accel->cache : nullptr;
+    unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint, cache);
+#else
     unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint);
     unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint);
-    if (likely (index == NOT_COVERED)) return_trace (false);
+#endif
+    if (index == NOT_COVERED) return_trace (false);
 
 
     hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
     hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
     skippy_iter.reset_fast (buffer->idx);
     skippy_iter.reset_fast (buffer->idx);
@@ -156,7 +194,7 @@ struct PairPosFormat1_3
         strip = true;
         strip = true;
       newFormats = compute_effective_value_formats (glyphset, strip, true);
       newFormats = compute_effective_value_formats (glyphset, strip, true);
     }
     }
-    
+
     out->valueFormat[0] = newFormats.first;
     out->valueFormat[0] = newFormats.first;
     out->valueFormat[1] = newFormats.second;
     out->valueFormat[1] = newFormats.second;
 
 

+ 56 - 2
thirdparty/harfbuzz/src/OT/Layout/GPOS/PairPosFormat2.hh

@@ -123,12 +123,61 @@ struct PairPosFormat2_4 : ValueBase
 
 
   const Coverage &get_coverage () const { return this+coverage; }
   const Coverage &get_coverage () const { return this+coverage; }
 
 
-  bool apply (hb_ot_apply_context_t *c) const
+  struct pair_pos_cache_t
+  {
+    hb_ot_lookup_cache_t coverage;
+    hb_ot_lookup_cache_t first;
+    hb_ot_lookup_cache_t second;
+  };
+
+  unsigned cache_cost () const
+  {
+    return (this+coverage).cost () + (this+classDef1).cost () + (this+classDef2).cost ();
+  }
+  static void * cache_func (void *p, hb_ot_lookup_cache_op_t op)
+  {
+    switch (op)
+    {
+      case hb_ot_lookup_cache_op_t::CREATE:
+      {
+	pair_pos_cache_t *cache = (pair_pos_cache_t *) hb_malloc (sizeof (pair_pos_cache_t));
+	if (likely (cache))
+	{
+	  cache->coverage.clear ();
+	  cache->first.clear ();
+	  cache->second.clear ();
+	}
+	return cache;
+      }
+      case hb_ot_lookup_cache_op_t::ENTER:
+	return (void *) true;
+      case hb_ot_lookup_cache_op_t::LEAVE:
+	return nullptr;
+      case hb_ot_lookup_cache_op_t::DESTROY:
+	{
+	  pair_pos_cache_t *cache = (pair_pos_cache_t *) p;
+	  hb_free (cache);
+	  return nullptr;
+	}
+    }
+    return nullptr;
+  }
+
+  bool apply_cached (hb_ot_apply_context_t *c) const { return _apply (c, true); }
+  bool apply (hb_ot_apply_context_t *c) const { return _apply (c, false); }
+  bool _apply (hb_ot_apply_context_t *c, bool cached) const
   {
   {
     TRACE_APPLY (this);
     TRACE_APPLY (this);
+
     hb_buffer_t *buffer = c->buffer;
     hb_buffer_t *buffer = c->buffer;
+
+#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
+    pair_pos_cache_t *cache = cached ? (pair_pos_cache_t *) c->lookup_accel->cache : nullptr;
+    unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint, cache ? &cache->coverage : nullptr);
+#else
     unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint);
     unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint);
-    if (likely (index == NOT_COVERED)) return_trace (false);
+#endif
+    if (index == NOT_COVERED) return_trace (false);
 
 
     hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
     hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
     skippy_iter.reset_fast (buffer->idx);
     skippy_iter.reset_fast (buffer->idx);
@@ -139,8 +188,13 @@ struct PairPosFormat2_4 : ValueBase
       return_trace (false);
       return_trace (false);
     }
     }
 
 
+#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
+    unsigned int klass1 = (this+classDef1).get_class (buffer->cur().codepoint, cache ? &cache->first : nullptr);
+    unsigned int klass2 = (this+classDef2).get_class (buffer->info[skippy_iter.idx].codepoint, cache ? &cache->second : nullptr);
+#else
     unsigned int klass1 = (this+classDef1).get_class (buffer->cur().codepoint);
     unsigned int klass1 = (this+classDef1).get_class (buffer->cur().codepoint);
     unsigned int klass2 = (this+classDef2).get_class (buffer->info[skippy_iter.idx].codepoint);
     unsigned int klass2 = (this+classDef2).get_class (buffer->info[skippy_iter.idx].codepoint);
+#endif
     if (unlikely (klass1 >= class1Count || klass2 >= class2Count))
     if (unlikely (klass1 >= class1Count || klass2 >= class2Count))
     {
     {
       buffer->unsafe_to_concat (buffer->idx, skippy_iter.idx + 1);
       buffer->unsafe_to_concat (buffer->idx, skippy_iter.idx + 1);

+ 1 - 1
thirdparty/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat1.hh

@@ -67,7 +67,7 @@ struct SinglePosFormat1 : ValueBase
     TRACE_APPLY (this);
     TRACE_APPLY (this);
     hb_buffer_t *buffer = c->buffer;
     hb_buffer_t *buffer = c->buffer;
     unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint);
     unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint);
-    if (likely (index == NOT_COVERED)) return_trace (false);
+    if (index == NOT_COVERED) return_trace (false);
 
 
     if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
     if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
     {
     {

+ 1 - 1
thirdparty/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat2.hh

@@ -66,7 +66,7 @@ struct SinglePosFormat2 : ValueBase
     TRACE_APPLY (this);
     TRACE_APPLY (this);
     hb_buffer_t *buffer = c->buffer;
     hb_buffer_t *buffer = c->buffer;
     unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint);
     unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint);
-    if (likely (index == NOT_COVERED)) return_trace (false);
+    if (index == NOT_COVERED) return_trace (false);
 
 
     if (unlikely (index >= valueCount)) return_trace (false);
     if (unlikely (index >= valueCount)) return_trace (false);
 
 

+ 1 - 1
thirdparty/harfbuzz/src/OT/Layout/GSUB/AlternateSubstFormat1.hh

@@ -74,7 +74,7 @@ struct AlternateSubstFormat1_2
     TRACE_APPLY (this);
     TRACE_APPLY (this);
 
 
     unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
     unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
-    if (likely (index == NOT_COVERED)) return_trace (false);
+    if (index == NOT_COVERED) return_trace (false);
 
 
     return_trace ((this+alternateSet[index]).apply (c));
     return_trace ((this+alternateSet[index]).apply (c));
   }
   }

+ 41 - 4
thirdparty/harfbuzz/src/OT/Layout/GSUB/LigatureSubstFormat1.hh

@@ -78,12 +78,49 @@ struct LigatureSubstFormat1_2
     return lig_set.would_apply (c);
     return lig_set.would_apply (c);
   }
   }
 
 
-  bool apply (hb_ot_apply_context_t *c) const
+  unsigned cache_cost () const
   {
   {
-    TRACE_APPLY (this);
+    return (this+coverage).cost ();
+  }
+  static void * cache_func (void *p, hb_ot_lookup_cache_op_t op)
+  {
+    switch (op)
+    {
+      case hb_ot_lookup_cache_op_t::CREATE:
+      {
+	hb_ot_lookup_cache_t *cache = (hb_ot_lookup_cache_t *) hb_malloc (sizeof (hb_ot_lookup_cache_t));
+	if (likely (cache))
+	  cache->clear ();
+	return cache;
+      }
+      case hb_ot_lookup_cache_op_t::ENTER:
+	return (void *) true;
+      case hb_ot_lookup_cache_op_t::LEAVE:
+	return nullptr;
+      case hb_ot_lookup_cache_op_t::DESTROY:
+      {
+	hb_ot_lookup_cache_t *cache = (hb_ot_lookup_cache_t *) p;
+	hb_free (cache);
+	return nullptr;
+      }
+    }
+    return nullptr;
+  }
 
 
-    unsigned int index = (this+coverage).get_coverage (c->buffer->cur ().codepoint);
-    if (likely (index == NOT_COVERED)) return_trace (false);
+  bool apply_cached (hb_ot_apply_context_t *c) const { return _apply (c, true); }
+  bool apply (hb_ot_apply_context_t *c) const { return _apply (c, false); }
+  bool _apply (hb_ot_apply_context_t *c, bool cached) const
+  {
+    TRACE_APPLY (this);
+    hb_buffer_t *buffer = c->buffer;
+
+#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
+    hb_ot_lookup_cache_t *cache = cached ? (hb_ot_lookup_cache_t *) c->lookup_accel->cache : nullptr;
+    unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint, cache);
+#else
+    unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint);
+#endif
+    if (index == NOT_COVERED) return_trace (false);
 
 
     const auto &lig_set = this+ligatureSet[index];
     const auto &lig_set = this+ligatureSet[index];
     return_trace (lig_set.apply (c));
     return_trace (lig_set.apply (c));

+ 1 - 1
thirdparty/harfbuzz/src/OT/Layout/GSUB/MultipleSubstFormat1.hh

@@ -66,7 +66,7 @@ struct MultipleSubstFormat1_2
     TRACE_APPLY (this);
     TRACE_APPLY (this);
 
 
     unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
     unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
-    if (likely (index == NOT_COVERED)) return_trace (false);
+    if (index == NOT_COVERED) return_trace (false);
 
 
     return_trace ((this+sequence[index]).apply (c));
     return_trace ((this+sequence[index]).apply (c));
   }
   }

+ 1 - 1
thirdparty/harfbuzz/src/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh

@@ -112,7 +112,7 @@ struct ReverseChainSingleSubstFormat1
   {
   {
     TRACE_APPLY (this);
     TRACE_APPLY (this);
     unsigned int index = (this+coverage).get_coverage (c->buffer->cur ().codepoint);
     unsigned int index = (this+coverage).get_coverage (c->buffer->cur ().codepoint);
-    if (likely (index == NOT_COVERED)) return_trace (false);
+    if (index == NOT_COVERED) return_trace (false);
 
 
     if (unlikely (c->nesting_level_left != HB_MAX_NESTING_LEVEL))
     if (unlikely (c->nesting_level_left != HB_MAX_NESTING_LEVEL))
       return_trace (false); /* No chaining to this type */
       return_trace (false); /* No chaining to this type */

+ 1 - 1
thirdparty/harfbuzz/src/OT/Layout/GSUB/SingleSubstFormat1.hh

@@ -128,7 +128,7 @@ struct SingleSubstFormat1_3
     TRACE_APPLY (this);
     TRACE_APPLY (this);
     hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
     hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
     unsigned int index = (this+coverage).get_coverage (glyph_id);
     unsigned int index = (this+coverage).get_coverage (glyph_id);
-    if (likely (index == NOT_COVERED)) return_trace (false);
+    if (index == NOT_COVERED) return_trace (false);
 
 
     hb_codepoint_t d = deltaGlyphID;
     hb_codepoint_t d = deltaGlyphID;
     hb_codepoint_t mask = get_mask ();
     hb_codepoint_t mask = get_mask ();

+ 1 - 1
thirdparty/harfbuzz/src/OT/Layout/GSUB/SingleSubstFormat2.hh

@@ -104,7 +104,7 @@ struct SingleSubstFormat2_4
   {
   {
     TRACE_APPLY (this);
     TRACE_APPLY (this);
     unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
     unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
-    if (likely (index == NOT_COVERED)) return_trace (false);
+    if (index == NOT_COVERED) return_trace (false);
 
 
     if (unlikely (index >= substitute.len)) return_trace (false);
     if (unlikely (index >= substitute.len)) return_trace (false);
 
 

+ 3 - 0
thirdparty/harfbuzz/src/OT/Layout/types.hh

@@ -29,6 +29,9 @@
 #ifndef OT_LAYOUT_TYPES_HH
 #ifndef OT_LAYOUT_TYPES_HH
 #define OT_LAYOUT_TYPES_HH
 #define OT_LAYOUT_TYPES_HH
 
 
+using hb_ot_lookup_cache_t = hb_cache_t<15, 8, 7>;
+static_assert (sizeof (hb_ot_lookup_cache_t) == 256, "");
+
 namespace OT {
 namespace OT {
 namespace Layout {
 namespace Layout {
 
 

+ 81 - 23
thirdparty/harfbuzz/src/OT/Var/VARC/VARC.cc

@@ -3,7 +3,6 @@
 #ifndef HB_NO_VAR_COMPOSITES
 #ifndef HB_NO_VAR_COMPOSITES
 
 
 #include "../../../hb-draw.hh"
 #include "../../../hb-draw.hh"
-#include "../../../hb-geometry.hh"
 #include "../../../hb-ot-layout-common.hh"
 #include "../../../hb-ot-layout-common.hh"
 #include "../../../hb-ot-layout-gdef-table.hh"
 #include "../../../hb-ot-layout-gdef-table.hh"
 
 
@@ -133,18 +132,19 @@ VarComponent::get_path_at (hb_font_t *font,
 			   hb_codepoint_t parent_gid,
 			   hb_codepoint_t parent_gid,
 			   hb_draw_session_t &draw_session,
 			   hb_draw_session_t &draw_session,
 			   hb_array_t<const int> coords,
 			   hb_array_t<const int> coords,
+			   hb_transform_t total_transform,
 			   hb_ubytes_t total_record,
 			   hb_ubytes_t total_record,
-			   hb_set_t *visited,
+			   hb_decycler_t *decycler,
 			   signed *edges_left,
 			   signed *edges_left,
 			   signed depth_left,
 			   signed depth_left,
+			   hb_glyf_scratch_t &scratch,
 			   VarRegionList::cache_t *cache) const
 			   VarRegionList::cache_t *cache) const
 {
 {
   const unsigned char *end = total_record.arrayZ + total_record.length;
   const unsigned char *end = total_record.arrayZ + total_record.length;
   const unsigned char *record = total_record.arrayZ;
   const unsigned char *record = total_record.arrayZ;
 
 
-  auto &VARC = *font->face->table.VARC;
+  auto &VARC = *font->face->table.VARC->table;
   auto &varStore = &VARC+VARC.varStore;
   auto &varStore = &VARC+VARC.varStore;
-  auto instancer = MultiItemVarStoreInstancer(&varStore, nullptr, coords, cache);
 
 
 #define READ_UINT32VAR(name) \
 #define READ_UINT32VAR(name) \
   HB_STMT_START { \
   HB_STMT_START { \
@@ -187,22 +187,25 @@ VarComponent::get_path_at (hb_font_t *font,
     unsigned conditionIndex;
     unsigned conditionIndex;
     READ_UINT32VAR (conditionIndex);
     READ_UINT32VAR (conditionIndex);
     const auto &condition = (&VARC+VARC.conditionList)[conditionIndex];
     const auto &condition = (&VARC+VARC.conditionList)[conditionIndex];
+    auto instancer = MultiItemVarStoreInstancer(&varStore, nullptr, coords, cache);
     show = condition.evaluate (coords.arrayZ, coords.length, &instancer);
     show = condition.evaluate (coords.arrayZ, coords.length, &instancer);
   }
   }
 
 
   // Axis values
   // Axis values
 
 
-  hb_vector_t<unsigned> axisIndices;
-  hb_vector_t<float> axisValues;
+  auto &axisIndices = scratch.axisIndices;
+  axisIndices.clear ();
+  auto &axisValues = scratch.axisValues;
+  axisValues.clear ();
   if (flags & (unsigned) flags_t::HAVE_AXES)
   if (flags & (unsigned) flags_t::HAVE_AXES)
   {
   {
     unsigned axisIndicesIndex;
     unsigned axisIndicesIndex;
     READ_UINT32VAR (axisIndicesIndex);
     READ_UINT32VAR (axisIndicesIndex);
-    axisIndices = (&VARC+VARC.axisIndicesList)[axisIndicesIndex];
+    axisIndices.extend ((&VARC+VARC.axisIndicesList)[axisIndicesIndex]);
     axisValues.resize (axisIndices.length);
     axisValues.resize (axisIndices.length);
     const HBUINT8 *p = (const HBUINT8 *) record;
     const HBUINT8 *p = (const HBUINT8 *) record;
     TupleValues::decompile (p, axisValues, (const HBUINT8 *) end);
     TupleValues::decompile (p, axisValues, (const HBUINT8 *) end);
-    record += (const unsigned char *) p - record;
+    record = (const unsigned char *) p;
   }
   }
 
 
   // Apply variations if any
   // Apply variations if any
@@ -312,32 +315,87 @@ VarComponent::get_path_at (hb_font_t *font,
     if (!(flags & (unsigned) flags_t::HAVE_SCALE_Y))
     if (!(flags & (unsigned) flags_t::HAVE_SCALE_Y))
       transform.scaleY = transform.scaleX;
       transform.scaleY = transform.scaleX;
 
 
-    // Scale the transform by the font's scale
-    float x_scale = font->x_multf;
-    float y_scale = font->y_multf;
-    transform.translateX *= x_scale;
-    transform.translateY *= y_scale;
-    transform.tCenterX *= x_scale;
-    transform.tCenterY *= y_scale;
+    total_transform.transform (transform.to_transform ());
+    total_transform.scale (font->x_mult ? 1.f / font->x_multf : 0.f,
+			   font->y_mult ? 1.f / font->y_multf : 0.f);
 
 
+    VARC.get_path_at (font, gid,
+		      draw_session, component_coords, total_transform,
+		      parent_gid,
+		      decycler, edges_left, depth_left - 1,
+		      scratch);
+  }
+
+#undef PROCESS_TRANSFORM_COMPONENTS
+#undef READ_UINT32VAR
+
+  return hb_ubytes_t (record, end - record);
+}
+
+bool
+VARC::get_path_at (hb_font_t *font,
+		   hb_codepoint_t glyph,
+		   hb_draw_session_t &draw_session,
+		   hb_array_t<const int> coords,
+		   hb_transform_t transform,
+		   hb_codepoint_t parent_glyph,
+		   hb_decycler_t *decycler,
+		   signed *edges_left,
+		   signed depth_left,
+		   hb_glyf_scratch_t &scratch) const
+{
+  // Don't recurse on the same glyph.
+  unsigned idx = glyph == parent_glyph ?
+		 NOT_COVERED :
+		 (this+coverage).get_coverage (glyph);
+  if (idx == NOT_COVERED)
+  {
     // Build a transforming pen to apply the transform.
     // Build a transforming pen to apply the transform.
     hb_draw_funcs_t *transformer_funcs = hb_transforming_pen_get_funcs ();
     hb_draw_funcs_t *transformer_funcs = hb_transforming_pen_get_funcs ();
-    hb_transforming_pen_context_t context {transform.to_transform (),
+    hb_transforming_pen_context_t context {transform,
 					   draw_session.funcs,
 					   draw_session.funcs,
 					   draw_session.draw_data,
 					   draw_session.draw_data,
 					   &draw_session.st};
 					   &draw_session.st};
     hb_draw_session_t transformer_session {transformer_funcs, &context};
     hb_draw_session_t transformer_session {transformer_funcs, &context};
+    hb_draw_session_t &shape_draw_session = transform.is_identity () ? draw_session : transformer_session;
 
 
-    VARC.get_path_at (font, gid,
-		      transformer_session, component_coords,
-		      parent_gid,
-		      visited, edges_left, depth_left - 1);
+    if (!font->face->table.glyf->get_path_at (font, glyph, shape_draw_session, coords, scratch))
+#ifndef HB_NO_CFF
+    if (!font->face->table.cff2->get_path_at (font, glyph, shape_draw_session, coords))
+    if (!font->face->table.cff1->get_path (font, glyph, shape_draw_session)) // Doesn't have variations
+#endif
+      return false;
+    return true;
   }
   }
 
 
-#undef PROCESS_TRANSFORM_COMPONENTS
-#undef READ_UINT32VAR
+  if (depth_left <= 0)
+    return true;
 
 
-  return hb_ubytes_t (record, end - record);
+  if (*edges_left <= 0)
+    return true;
+  (*edges_left)--;
+
+  hb_decycler_node_t node (*decycler);
+  if (unlikely (!node.visit (glyph)))
+    return true;
+
+  hb_ubytes_t record = (this+glyphRecords)[idx];
+
+  float static_cache[sizeof (void *) * 16];
+  VarRegionList::cache_t *cache = (this+varStore).create_cache (hb_array (static_cache));
+
+  transform.scale (font->x_multf, font->y_multf);
+
+  VarCompositeGlyph::get_path_at (font, glyph,
+				  draw_session, coords, transform,
+				  record,
+				  decycler, edges_left, depth_left,
+				  scratch,
+				  cache);
+
+  (this+varStore).destroy_cache (cache, hb_array (static_cache));
+
+  return true;
 }
 }
 
 
 //} // namespace Var
 //} // namespace Var

+ 95 - 70
thirdparty/harfbuzz/src/OT/Var/VARC/VARC.hh

@@ -1,6 +1,8 @@
 #ifndef OT_VAR_VARC_VARC_HH
 #ifndef OT_VAR_VARC_VARC_HH
 #define OT_VAR_VARC_VARC_HH
 #define OT_VAR_VARC_VARC_HH
 
 
+#include "../../../hb-decycler.hh"
+#include "../../../hb-geometry.hh"
 #include "../../../hb-ot-layout-common.hh"
 #include "../../../hb-ot-layout-common.hh"
 #include "../../../hb-ot-glyf-table.hh"
 #include "../../../hb-ot-glyf-table.hh"
 #include "../../../hb-ot-cff2-table.hh"
 #include "../../../hb-ot-cff2-table.hh"
@@ -46,10 +48,12 @@ struct VarComponent
 	       hb_codepoint_t parent_gid,
 	       hb_codepoint_t parent_gid,
 	       hb_draw_session_t &draw_session,
 	       hb_draw_session_t &draw_session,
 	       hb_array_t<const int> coords,
 	       hb_array_t<const int> coords,
+	       hb_transform_t transform,
 	       hb_ubytes_t record,
 	       hb_ubytes_t record,
-	       hb_set_t *visited,
+	       hb_decycler_t *decycler,
 	       signed *edges_left,
 	       signed *edges_left,
 	       signed depth_left,
 	       signed depth_left,
+	       hb_glyf_scratch_t &scratch,
 	       VarRegionList::cache_t *cache = nullptr) const;
 	       VarRegionList::cache_t *cache = nullptr) const;
 };
 };
 
 
@@ -60,19 +64,21 @@ struct VarCompositeGlyph
 	       hb_codepoint_t glyph,
 	       hb_codepoint_t glyph,
 	       hb_draw_session_t &draw_session,
 	       hb_draw_session_t &draw_session,
 	       hb_array_t<const int> coords,
 	       hb_array_t<const int> coords,
+	       hb_transform_t transform,
 	       hb_ubytes_t record,
 	       hb_ubytes_t record,
-	       hb_set_t *visited,
+	       hb_decycler_t *decycler,
 	       signed *edges_left,
 	       signed *edges_left,
 	       signed depth_left,
 	       signed depth_left,
+	       hb_glyf_scratch_t &scratch,
 	       VarRegionList::cache_t *cache = nullptr)
 	       VarRegionList::cache_t *cache = nullptr)
   {
   {
     while (record)
     while (record)
     {
     {
       const VarComponent &comp = * (const VarComponent *) (record.arrayZ);
       const VarComponent &comp = * (const VarComponent *) (record.arrayZ);
       record = comp.get_path_at (font, glyph,
       record = comp.get_path_at (font, glyph,
-				 draw_session, coords,
+				 draw_session, coords, transform,
 				 record,
 				 record,
-				 visited, edges_left, depth_left, cache);
+				 decycler, edges_left, depth_left, scratch, cache);
     }
     }
   }
   }
 };
 };
@@ -85,79 +91,37 @@ struct VARC
 
 
   static constexpr hb_tag_t tableTag = HB_TAG ('V', 'A', 'R', 'C');
   static constexpr hb_tag_t tableTag = HB_TAG ('V', 'A', 'R', 'C');
 
 
-  bool
+  HB_INTERNAL bool
   get_path_at (hb_font_t *font,
   get_path_at (hb_font_t *font,
 	       hb_codepoint_t glyph,
 	       hb_codepoint_t glyph,
 	       hb_draw_session_t &draw_session,
 	       hb_draw_session_t &draw_session,
 	       hb_array_t<const int> coords,
 	       hb_array_t<const int> coords,
-	       hb_codepoint_t parent_glyph = HB_CODEPOINT_INVALID,
-	       hb_set_t *visited = nullptr,
-	       signed *edges_left = nullptr,
-	       signed depth_left = HB_MAX_NESTING_LEVEL) const
-  {
-    hb_set_t stack_set;
-    if (visited == nullptr)
-      visited = &stack_set;
-    signed stack_edges = HB_MAX_GRAPH_EDGE_COUNT;
-    if (edges_left == nullptr)
-      edges_left = &stack_edges;
-
-    // Don't recurse on the same glyph.
-    unsigned idx = glyph == parent_glyph ?
-		   NOT_COVERED :
-		   (this+coverage).get_coverage (glyph);
-    if (idx == NOT_COVERED)
-    {
-      if (!font->face->table.glyf->get_path_at (font, glyph, draw_session, coords))
-#ifndef HB_NO_CFF
-      if (!font->face->table.cff2->get_path_at (font, glyph, draw_session, coords))
-      if (!font->face->table.cff1->get_path (font, glyph, draw_session)) // Doesn't have variations
-#endif
-	return false;
-      return true;
-    }
-
-    if (depth_left <= 0)
-      return true;
-
-    if (*edges_left <= 0)
-      return true;
-    (*edges_left)--;
-
-    if (visited->has (glyph) || visited->in_error ())
-      return true;
-    visited->add (glyph);
-
-    hb_ubytes_t record = (this+glyphRecords)[idx];
-
-    VarRegionList::cache_t *cache = record.length >= 64 ? // Heuristic
-				   (this+varStore).create_cache ()
-				   : nullptr;
-
-    VarCompositeGlyph::get_path_at (font, glyph,
-				    draw_session, coords,
-				    record,
-				    visited, edges_left, depth_left,
-				    cache);
-
-    (this+varStore).destroy_cache (cache);
-
-    visited->del (glyph);
-
-    return true;
-  }
+	       hb_transform_t transform,
+	       hb_codepoint_t parent_glyph,
+	       hb_decycler_t *decycler,
+	       signed *edges_left,
+	       signed depth_left,
+	       hb_glyf_scratch_t &scratch) const;
 
 
   bool
   bool
-  get_path (hb_font_t *font, hb_codepoint_t gid, hb_draw_session_t &draw_session) const
-  { return get_path_at (font, gid, draw_session, hb_array (font->coords, font->num_coords)); }
-
-  bool paint_glyph (hb_font_t *font, hb_codepoint_t gid, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const
+  get_path (hb_font_t *font,
+	    hb_codepoint_t gid,
+	    hb_draw_session_t &draw_session,
+	    hb_glyf_scratch_t &scratch) const
   {
   {
-    funcs->push_clip_glyph (data, gid, font);
-    funcs->color (data, true, foreground);
-    funcs->pop_clip (data);
-
-    return true;
+    hb_decycler_t decycler;
+    signed edges = HB_MAX_GRAPH_EDGE_COUNT;
+
+    return get_path_at (font,
+			gid,
+			draw_session,
+			hb_array (font->coords, font->num_coords),
+			HB_TRANSFORM_IDENTITY,
+			HB_CODEPOINT_INVALID,
+			&decycler,
+			&edges,
+			HB_MAX_NESTING_LEVEL,
+			scratch);
   }
   }
 
 
   bool sanitize (hb_sanitize_context_t *c) const
   bool sanitize (hb_sanitize_context_t *c) const
@@ -173,6 +137,63 @@ struct VARC
 		  glyphRecords.sanitize (c, this));
 		  glyphRecords.sanitize (c, this));
   }
   }
 
 
+  struct accelerator_t
+  {
+    friend struct VarComponent;
+
+    accelerator_t (hb_face_t *face)
+    {
+      table = hb_sanitize_context_t ().reference_table<VARC> (face);
+    }
+    ~accelerator_t ()
+    {
+      auto *scratch = cached_scratch.get_relaxed ();
+      if (scratch)
+      {
+	scratch->~hb_glyf_scratch_t ();
+	hb_free (scratch);
+      }
+
+      table.destroy ();
+    }
+
+    bool
+    get_path (hb_font_t *font, hb_codepoint_t gid, hb_draw_session_t &draw_session) const
+    {
+      if (!table->has_data ()) return false;
+
+      hb_glyf_scratch_t *scratch;
+
+      // Borrow the cached strach buffer.
+      {
+	scratch = cached_scratch.get_acquire ();
+	if (!scratch || unlikely (!cached_scratch.cmpexch (scratch, nullptr)))
+	{
+	  scratch = (hb_glyf_scratch_t *) hb_calloc (1, sizeof (hb_glyf_scratch_t));
+	  if (unlikely (!scratch))
+	    return true;
+	}
+      }
+
+      bool ret = table->get_path (font, gid, draw_session, *scratch);
+
+      // Put it back.
+      if (!cached_scratch.cmpexch (nullptr, scratch))
+      {
+        scratch->~hb_glyf_scratch_t ();
+	hb_free (scratch);
+      }
+
+      return ret;
+    }
+
+    private:
+    hb_blob_ptr_t<VARC> table;
+    hb_atomic_ptr_t<hb_glyf_scratch_t> cached_scratch;
+  };
+
+  bool has_data () const { return version.major != 0; }
+
   protected:
   protected:
   FixedVersion<> version; /* Version identifier */
   FixedVersion<> version; /* Version identifier */
   Offset32To<Coverage> coverage;
   Offset32To<Coverage> coverage;
@@ -184,6 +205,10 @@ struct VARC
   DEFINE_SIZE_STATIC (24);
   DEFINE_SIZE_STATIC (24);
 };
 };
 
 
+struct VARC_accelerator_t : VARC::accelerator_t {
+  VARC_accelerator_t (hb_face_t *face) : VARC::accelerator_t (face) {}
+};
+
 #endif
 #endif
 
 
 //}
 //}

+ 33 - 7
thirdparty/harfbuzz/src/OT/Var/VARC/coord-setter.hh

@@ -11,22 +11,48 @@ namespace OT {
 
 
 struct coord_setter_t
 struct coord_setter_t
 {
 {
-  coord_setter_t (hb_array_t<const int> coords) :
-    coords (coords) {}
+  coord_setter_t (hb_array_t<const int> coords_)
+  {
+    length = coords_.length;
+    if (length <= ARRAY_LENGTH (static_coords))
+      hb_memcpy (static_coords, coords_.arrayZ, length * sizeof (int));
+    else
+      dynamic_coords.extend (coords_);
+  }
 
 
   int& operator [] (unsigned idx)
   int& operator [] (unsigned idx)
   {
   {
     if (unlikely (idx >= HB_VAR_COMPOSITE_MAX_AXES))
     if (unlikely (idx >= HB_VAR_COMPOSITE_MAX_AXES))
       return Crap(int);
       return Crap(int);
-    if (coords.length < idx + 1)
-      coords.resize (idx + 1);
-    return coords[idx];
+
+    if (length <= ARRAY_LENGTH (static_coords))
+    {
+      if (idx < ARRAY_LENGTH (static_coords))
+      {
+        while (length <= idx)
+	  static_coords[length++] = 0;
+	return static_coords[idx];
+      }
+      else
+        dynamic_coords.extend (hb_array (static_coords, length));
+    }
+
+    if (dynamic_coords.length <= idx)
+    {
+      if (unlikely (!dynamic_coords.resize (idx + 1)))
+	return Crap(int);
+      length = idx + 1;
+    }
+    return dynamic_coords.arrayZ[idx];
   }
   }
 
 
   hb_array_t<int> get_coords ()
   hb_array_t<int> get_coords ()
-  { return coords.as_array (); }
+  { return length <= ARRAY_LENGTH (static_coords) ? hb_array (static_coords, length) : dynamic_coords.as_array (); }
 
 
-  hb_vector_t<int> coords;
+  private:
+  hb_vector_t<int> dynamic_coords;
+  unsigned length;
+  int static_coords[sizeof (void *) * 8];
 };
 };
 
 
 
 

+ 1 - 1
thirdparty/harfbuzz/src/OT/glyf/CompositeGlyph.hh

@@ -143,7 +143,7 @@ struct CompositeGlyphRecord
     float matrix[4];
     float matrix[4];
     contour_point_t trans;
     contour_point_t trans;
     get_transformation (matrix, trans);
     get_transformation (matrix, trans);
-    if (unlikely (!points.alloc (points.length + 4))) return false; // For phantom points
+    if (unlikely (!points.alloc (points.length + 1 + 4))) return false; // For phantom points
     points.push (trans);
     points.push (trans);
     return true;
     return true;
   }
   }

+ 42 - 24
thirdparty/harfbuzz/src/OT/glyf/Glyph.hh

@@ -251,7 +251,8 @@ struct Glyph
       composite_contours_p = nullptr;
       composite_contours_p = nullptr;
     }
     }
 
 
-    if (!get_points (font, glyf, all_points, &points_with_deltas, head_maxp_info_p, composite_contours_p, false, false))
+    hb_glyf_scratch_t scratch;
+    if (!get_points (font, glyf, all_points, scratch, &points_with_deltas, head_maxp_info_p, composite_contours_p, false, false))
       return false;
       return false;
 
 
     // .notdef, set type to empty so we only update metrics and don't compile bytes for
     // .notdef, set type to empty so we only update metrics and don't compile bytes for
@@ -305,6 +306,7 @@ struct Glyph
   template <typename accelerator_t>
   template <typename accelerator_t>
   bool get_points (hb_font_t *font, const accelerator_t &glyf_accelerator,
   bool get_points (hb_font_t *font, const accelerator_t &glyf_accelerator,
 		   contour_point_vector_t &all_points /* OUT */,
 		   contour_point_vector_t &all_points /* OUT */,
+		   hb_glyf_scratch_t &scratch,
 		   contour_point_vector_t *points_with_deltas = nullptr, /* OUT */
 		   contour_point_vector_t *points_with_deltas = nullptr, /* OUT */
 		   head_maxp_info_t * head_maxp_info = nullptr, /* OUT */
 		   head_maxp_info_t * head_maxp_info = nullptr, /* OUT */
 		   unsigned *composite_contours = nullptr, /* OUT */
 		   unsigned *composite_contours = nullptr, /* OUT */
@@ -312,7 +314,6 @@ struct Glyph
 		   bool use_my_metrics = true,
 		   bool use_my_metrics = true,
 		   bool phantom_only = false,
 		   bool phantom_only = false,
 		   hb_array_t<const int> coords = hb_array_t<const int> (),
 		   hb_array_t<const int> coords = hb_array_t<const int> (),
-		   hb_map_t *current_glyphs = nullptr,
 		   unsigned int depth = 0,
 		   unsigned int depth = 0,
 		   unsigned *edge_count = nullptr) const
 		   unsigned *edge_count = nullptr) const
   {
   {
@@ -322,10 +323,6 @@ struct Glyph
     if (unlikely (*edge_count > HB_MAX_GRAPH_EDGE_COUNT)) return false;
     if (unlikely (*edge_count > HB_MAX_GRAPH_EDGE_COUNT)) return false;
     (*edge_count)++;
     (*edge_count)++;
 
 
-    hb_map_t current_glyphs_stack;
-    if (current_glyphs == nullptr)
-      current_glyphs = &current_glyphs_stack;
-
     if (head_maxp_info)
     if (head_maxp_info)
     {
     {
       head_maxp_info->maxComponentDepth = hb_max (head_maxp_info->maxComponentDepth, depth);
       head_maxp_info->maxComponentDepth = hb_max (head_maxp_info->maxComponentDepth, depth);
@@ -334,8 +331,7 @@ struct Glyph
     if (!coords)
     if (!coords)
       coords = hb_array (font->coords, font->num_coords);
       coords = hb_array (font->coords, font->num_coords);
 
 
-    contour_point_vector_t stack_points;
-    contour_point_vector_t &points = type == SIMPLE ? all_points : stack_points;
+    contour_point_vector_t &points = type == SIMPLE ? all_points : scratch.comp_points;
     unsigned old_length = points.length;
     unsigned old_length = points.length;
 
 
     switch (type) {
     switch (type) {
@@ -388,36 +384,53 @@ struct Glyph
 
 
 #ifndef HB_NO_VAR
 #ifndef HB_NO_VAR
     if (coords)
     if (coords)
-      glyf_accelerator.gvar->apply_deltas_to_points (gid,
-						     coords,
-						     points.as_array ().sub_array (old_length),
-						     phantom_only && type == SIMPLE);
+    {
+#ifndef HB_NO_BEYOND_64K
+      if (glyf_accelerator.GVAR->has_data ())
+	glyf_accelerator.GVAR->apply_deltas_to_points (gid,
+						       coords,
+						       points.as_array ().sub_array (old_length),
+						       scratch,
+						       phantom_only && type == SIMPLE);
+      else
+#endif
+	glyf_accelerator.gvar->apply_deltas_to_points (gid,
+						       coords,
+						       points.as_array ().sub_array (old_length),
+						       scratch,
+						       phantom_only && type == SIMPLE);
+    }
 #endif
 #endif
 
 
     // mainly used by CompositeGlyph calculating new X/Y offset value so no need to extend it
     // mainly used by CompositeGlyph calculating new X/Y offset value so no need to extend it
     // with child glyphs' points
     // with child glyphs' points
     if (points_with_deltas != nullptr && depth == 0 && type == COMPOSITE)
     if (points_with_deltas != nullptr && depth == 0 && type == COMPOSITE)
     {
     {
-      if (unlikely (!points_with_deltas->resize (points.length))) return false;
+      assert (old_length == 0);
       *points_with_deltas = points;
       *points_with_deltas = points;
     }
     }
 
 
+    float shift = 0;
     switch (type) {
     switch (type) {
     case SIMPLE:
     case SIMPLE:
       if (depth == 0 && head_maxp_info)
       if (depth == 0 && head_maxp_info)
         head_maxp_info->maxPoints = hb_max (head_maxp_info->maxPoints, all_points.length - old_length - 4);
         head_maxp_info->maxPoints = hb_max (head_maxp_info->maxPoints, all_points.length - old_length - 4);
+      shift = phantoms[PHANTOM_LEFT].x;
       break;
       break;
     case COMPOSITE:
     case COMPOSITE:
     {
     {
+      hb_decycler_node_t decycler_node (scratch.decycler);
+
       unsigned int comp_index = 0;
       unsigned int comp_index = 0;
       for (auto &item : get_composite_iterator ())
       for (auto &item : get_composite_iterator ())
       {
       {
 	hb_codepoint_t item_gid = item.get_gid ();
 	hb_codepoint_t item_gid = item.get_gid ();
 
 
-        if (unlikely (current_glyphs->has (item_gid)))
+        if (unlikely (!decycler_node.visit (item_gid)))
+	{
+	  comp_index++;
 	  continue;
 	  continue;
-
-	current_glyphs->add (item_gid);
+	}
 
 
 	unsigned old_count = all_points.length;
 	unsigned old_count = all_points.length;
 
 
@@ -426,6 +439,7 @@ struct Glyph
 				       .get_points (font,
 				       .get_points (font,
 						    glyf_accelerator,
 						    glyf_accelerator,
 						    all_points,
 						    all_points,
+						    scratch,
 						    points_with_deltas,
 						    points_with_deltas,
 						    head_maxp_info,
 						    head_maxp_info,
 						    composite_contours,
 						    composite_contours,
@@ -433,14 +447,16 @@ struct Glyph
 						    use_my_metrics,
 						    use_my_metrics,
 						    phantom_only,
 						    phantom_only,
 						    coords,
 						    coords,
-						    current_glyphs,
 						    depth + 1,
 						    depth + 1,
 						    edge_count)))
 						    edge_count)))
 	{
 	{
-	  current_glyphs->del (item_gid);
+	  points.resize (old_length);
 	  return false;
 	  return false;
 	}
 	}
 
 
+	// points might have been reallocated. Relocate phantoms.
+	phantoms = points.as_array ().sub_array (points.length - PHANTOM_COUNT, PHANTOM_COUNT);
+
 	auto comp_points = all_points.as_array ().sub_array (old_count);
 	auto comp_points = all_points.as_array ().sub_array (old_count);
 
 
 	/* Copy phantom points from component if USE_MY_METRICS flag set */
 	/* Copy phantom points from component if USE_MY_METRICS flag set */
@@ -455,7 +471,7 @@ struct Glyph
 	  item.get_transformation (matrix, default_trans);
 	  item.get_transformation (matrix, default_trans);
 
 
 	  /* Apply component transformation & translation (with deltas applied) */
 	  /* Apply component transformation & translation (with deltas applied) */
-	  item.transform_points (comp_points, matrix, points[comp_index]);
+	  item.transform_points (comp_points, matrix, points[old_length + comp_index]);
 	}
 	}
 
 
 	if (item.is_anchored () && !phantom_only)
 	if (item.is_anchored () && !phantom_only)
@@ -476,12 +492,11 @@ struct Glyph
 
 
 	if (all_points.length > HB_GLYF_MAX_POINTS)
 	if (all_points.length > HB_GLYF_MAX_POINTS)
 	{
 	{
-	  current_glyphs->del (item_gid);
+	  points.resize (old_length);
 	  return false;
 	  return false;
 	}
 	}
 
 
 	comp_index++;
 	comp_index++;
-        current_glyphs->del (item_gid);
       }
       }
 
 
       if (head_maxp_info && depth == 0)
       if (head_maxp_info && depth == 0)
@@ -492,9 +507,13 @@ struct Glyph
         head_maxp_info->maxComponentElements = hb_max (head_maxp_info->maxComponentElements, comp_index);
         head_maxp_info->maxComponentElements = hb_max (head_maxp_info->maxComponentElements, comp_index);
       }
       }
       all_points.extend (phantoms);
       all_points.extend (phantoms);
+      shift = phantoms[PHANTOM_LEFT].x;
+      points.resize (old_length);
     } break;
     } break;
     case EMPTY:
     case EMPTY:
       all_points.extend (phantoms);
       all_points.extend (phantoms);
+      shift = phantoms[PHANTOM_LEFT].x;
+      points.resize (old_length);
       break;
       break;
     }
     }
 
 
@@ -503,10 +522,9 @@ struct Glyph
       /* Undocumented rasterizer behavior:
       /* Undocumented rasterizer behavior:
        * Shift points horizontally by the updated left side bearing
        * Shift points horizontally by the updated left side bearing
        */
        */
-      float v = -phantoms[PHANTOM_LEFT].x;
-      if (v)
+      if (shift)
         for (auto &point : all_points)
         for (auto &point : all_points)
-	  point.x += v;
+	  point.x -= shift;
     }
     }
 
 
     return !all_points.in_error ();
     return !all_points.in_error ();

+ 8 - 10
thirdparty/harfbuzz/src/OT/glyf/SimpleGlyph.hh

@@ -127,19 +127,20 @@ struct SimpleGlyph
 			  hb_array_t<contour_point_t> points_ /* IN/OUT */,
 			  hb_array_t<contour_point_t> points_ /* IN/OUT */,
 			  const HBUINT8 *end)
 			  const HBUINT8 *end)
   {
   {
+    auto *points = points_.arrayZ;
     unsigned count = points_.length;
     unsigned count = points_.length;
     for (unsigned int i = 0; i < count;)
     for (unsigned int i = 0; i < count;)
     {
     {
       if (unlikely (p + 1 > end)) return false;
       if (unlikely (p + 1 > end)) return false;
       uint8_t flag = *p++;
       uint8_t flag = *p++;
-      points_.arrayZ[i++].flag = flag;
+      points[i++].flag = flag;
       if (flag & FLAG_REPEAT)
       if (flag & FLAG_REPEAT)
       {
       {
 	if (unlikely (p + 1 > end)) return false;
 	if (unlikely (p + 1 > end)) return false;
 	unsigned int repeat_count = *p++;
 	unsigned int repeat_count = *p++;
 	unsigned stop = hb_min (i + repeat_count, count);
 	unsigned stop = hb_min (i + repeat_count, count);
 	for (; i < stop; i++)
 	for (; i < stop; i++)
-	  points_.arrayZ[i].flag = flag;
+	  points[i].flag = flag;
       }
       }
     }
     }
     return true;
     return true;
@@ -160,10 +161,7 @@ struct SimpleGlyph
       if (flag & short_flag)
       if (flag & short_flag)
       {
       {
 	if (unlikely (p + 1 > end)) return false;
 	if (unlikely (p + 1 > end)) return false;
-	if (flag & same_flag)
-	  v += *p++;
-	else
-	  v -= *p++;
+	v += (bool(flag & same_flag) * 2 - 1) * *p++;
       }
       }
       else
       else
       {
       {
@@ -190,7 +188,7 @@ struct SimpleGlyph
     unsigned int num_points = endPtsOfContours[num_contours - 1] + 1;
     unsigned int num_points = endPtsOfContours[num_contours - 1] + 1;
 
 
     unsigned old_length = points.length;
     unsigned old_length = points.length;
-    points.alloc (points.length + num_points + 4, true); // Allocate for phantom points, to avoid a possible copy
+    points.alloc (points.length + num_points + 4); // Allocate for phantom points, to avoid a possible copy
     if (unlikely (!points.resize (points.length + num_points, false))) return false;
     if (unlikely (!points.resize (points.length + num_points, false))) return false;
     auto points_ = points.as_array ().sub_array (old_length);
     auto points_ = points.as_array ().sub_array (old_length);
     if (!phantom_only)
     if (!phantom_only)
@@ -281,9 +279,9 @@ struct SimpleGlyph
     unsigned num_points = all_points.length - 4;
     unsigned num_points = all_points.length - 4;
 
 
     hb_vector_t<uint8_t> flags, x_coords, y_coords;
     hb_vector_t<uint8_t> flags, x_coords, y_coords;
-    if (unlikely (!flags.alloc (num_points, true))) return false;
-    if (unlikely (!x_coords.alloc (2*num_points, true))) return false;
-    if (unlikely (!y_coords.alloc (2*num_points, true))) return false;
+    if (unlikely (!flags.alloc_exact (num_points))) return false;
+    if (unlikely (!x_coords.alloc_exact (2*num_points))) return false;
+    if (unlikely (!y_coords.alloc_exact (2*num_points))) return false;
 
 
     unsigned lastflag = 255, repeat = 0;
     unsigned lastflag = 255, repeat = 0;
     int prev_x = 0, prev_y = 0;
     int prev_x = 0, prev_y = 0;

+ 133 - 30
thirdparty/harfbuzz/src/OT/glyf/glyf.hh

@@ -94,7 +94,7 @@ struct glyf
     }
     }
 
 
     hb_vector_t<unsigned> padded_offsets;
     hb_vector_t<unsigned> padded_offsets;
-    if (unlikely (!padded_offsets.alloc (c->plan->new_to_old_gid_list.length, true)))
+    if (unlikely (!padded_offsets.alloc_exact (c->plan->new_to_old_gid_list.length)))
       return_trace (false);
       return_trace (false);
 
 
     hb_vector_t<glyf_impl::SubsetGlyph> glyphs;
     hb_vector_t<glyf_impl::SubsetGlyph> glyphs;
@@ -172,6 +172,9 @@ struct glyf_accelerator_t
     glyf_table = nullptr;
     glyf_table = nullptr;
 #ifndef HB_NO_VAR
 #ifndef HB_NO_VAR
     gvar = nullptr;
     gvar = nullptr;
+#ifndef HB_NO_BEYOND_64K
+    GVAR = nullptr;
+#endif
 #endif
 #endif
     hmtx = nullptr;
     hmtx = nullptr;
 #ifndef HB_NO_VERTICAL
 #ifndef HB_NO_VERTICAL
@@ -187,6 +190,9 @@ struct glyf_accelerator_t
     glyf_table = hb_sanitize_context_t ().reference_table<glyf> (face);
     glyf_table = hb_sanitize_context_t ().reference_table<glyf> (face);
 #ifndef HB_NO_VAR
 #ifndef HB_NO_VAR
     gvar = face->table.gvar;
     gvar = face->table.gvar;
+#ifndef HB_NO_BEYOND_64K
+    GVAR = face->table.GVAR;
+#endif
 #endif
 #endif
     hmtx = face->table.hmtx;
     hmtx = face->table.hmtx;
 #ifndef HB_NO_VERTICAL
 #ifndef HB_NO_VERTICAL
@@ -198,6 +204,13 @@ struct glyf_accelerator_t
   }
   }
   ~glyf_accelerator_t ()
   ~glyf_accelerator_t ()
   {
   {
+    auto *scratch = cached_scratch.get_relaxed ();
+    if (scratch)
+    {
+      scratch->~hb_glyf_scratch_t ();
+      hb_free (scratch);
+    }
+
     glyf_table.destroy ();
     glyf_table.destroy ();
   }
   }
 
 
@@ -206,21 +219,16 @@ struct glyf_accelerator_t
   protected:
   protected:
   template<typename T>
   template<typename T>
   bool get_points (hb_font_t *font, hb_codepoint_t gid, T consumer,
   bool get_points (hb_font_t *font, hb_codepoint_t gid, T consumer,
-		   hb_array_t<const int> coords = hb_array_t<const int> ()) const
+		   hb_array_t<const int> coords,
+		   hb_glyf_scratch_t &scratch) const
   {
   {
-    if (!coords)
-      coords = hb_array (font->coords, font->num_coords);
-
     if (gid >= num_glyphs) return false;
     if (gid >= num_glyphs) return false;
 
 
-    /* Making this allocfree is not that easy
-       https://github.com/harfbuzz/harfbuzz/issues/2095
-       mostly because of gvar handling in VF fonts,
-       perhaps a separate path for non-VF fonts can be considered */
-    contour_point_vector_t all_points;
+    auto &all_points = scratch.all_points;
+    all_points.resize (0);
 
 
     bool phantom_only = !consumer.is_consuming_contour_points ();
     bool phantom_only = !consumer.is_consuming_contour_points ();
-    if (unlikely (!glyph_for_gid (gid).get_points (font, *this, all_points, nullptr, nullptr, nullptr, true, true, phantom_only, coords)))
+    if (unlikely (!glyph_for_gid (gid).get_points (font, *this, all_points, scratch, nullptr, nullptr, nullptr, true, true, phantom_only, coords)))
       return false;
       return false;
 
 
     unsigned count = all_points.length;
     unsigned count = all_points.length;
@@ -229,8 +237,61 @@ struct glyf_accelerator_t
 
 
     if (consumer.is_consuming_contour_points ())
     if (consumer.is_consuming_contour_points ())
     {
     {
-      for (auto &point : all_points.as_array ().sub_array (0, count))
-	consumer.consume_point (point);
+      auto *points = all_points.arrayZ;
+
+      if (false)
+      {
+	/* Our path-builder was designed to work with this simple loop.
+	 * But FreeType and CoreText do it differently, so we match those
+	 * with the other, more complicated, code branch below. */
+	for (unsigned i = 0; i < count; i++)
+	{
+	  consumer.consume_point (points[i]);
+	  if (points[i].is_end_point)
+	    consumer.contour_end ();
+	}
+      }
+      else
+      {
+	for (unsigned i = 0; i < count; i++)
+	{
+	  // Start of a contour.
+	  if (points[i].flag & glyf_impl::SimpleGlyph::FLAG_ON_CURVE)
+	  {
+	    // First point is on-curve. Draw the contour.
+	    for (; i < count; i++)
+	    {
+	      consumer.consume_point (points[i]);
+	      if (points[i].is_end_point)
+	      {
+		consumer.contour_end ();
+		break;
+	      }
+	    }
+	  }
+	  else
+	  {
+	    unsigned start = i;
+
+	    // Find end of the contour.
+	    for (; i < count; i++)
+	      if (points[i].is_end_point)
+		break;
+
+	    unsigned end = i;
+
+	    // Enough to start from the end. Our path-builder takes care of the rest.
+	    if (likely (end < count)) // Can only fail in case of alloc failure *maybe*.
+	      consumer.consume_point (points[end]);
+
+	    for (i = start; i < end; i++)
+	      consumer.consume_point (points[i]);
+
+	    consumer.contour_end ();
+	  }
+	}
+      }
+
       consumer.points_end ();
       consumer.points_end ();
     }
     }
 
 
@@ -303,6 +364,7 @@ struct glyf_accelerator_t
 
 
     HB_ALWAYS_INLINE
     HB_ALWAYS_INLINE
     void consume_point (const contour_point_t &point) { bounds.add (point); }
     void consume_point (const contour_point_t &point) { bounds.add (point); }
+    void contour_end () {}
     void points_end () { bounds.get_extents (font, extents, scaled); }
     void points_end () { bounds.get_extents (font, extents, scaled); }
 
 
     bool is_consuming_contour_points () { return extents; }
     bool is_consuming_contour_points () { return extents; }
@@ -318,7 +380,12 @@ struct glyf_accelerator_t
 
 
     contour_point_t phantoms[glyf_impl::PHANTOM_COUNT];
     contour_point_t phantoms[glyf_impl::PHANTOM_COUNT];
     if (font->num_coords)
     if (font->num_coords)
-      success = get_points (font, gid, points_aggregator_t (font, nullptr, phantoms, false));
+    {
+      hb_glyf_scratch_t scratch;
+      success = get_points (font, gid, points_aggregator_t (font, nullptr, phantoms, false),
+			    hb_array (font->coords, font->num_coords),
+			    scratch);
+    }
 
 
     if (unlikely (!success))
     if (unlikely (!success))
       return
       return
@@ -338,9 +405,11 @@ struct glyf_accelerator_t
     if (unlikely (gid >= num_glyphs)) return false;
     if (unlikely (gid >= num_glyphs)) return false;
 
 
     hb_glyph_extents_t extents;
     hb_glyph_extents_t extents;
-
+    hb_glyf_scratch_t scratch;
     contour_point_t phantoms[glyf_impl::PHANTOM_COUNT];
     contour_point_t phantoms[glyf_impl::PHANTOM_COUNT];
-    if (unlikely (!get_points (font, gid, points_aggregator_t (font, &extents, phantoms, false))))
+    if (unlikely (!get_points (font, gid, points_aggregator_t (font, &extents, phantoms, false),
+			       hb_array (font->coords, font->num_coords),
+			       scratch)))
       return false;
       return false;
 
 
     *lsb = is_vertical
     *lsb = is_vertical
@@ -366,20 +435,16 @@ struct glyf_accelerator_t
 
 
 #ifndef HB_NO_VAR
 #ifndef HB_NO_VAR
     if (font->num_coords)
     if (font->num_coords)
-      return get_points (font, gid, points_aggregator_t (font, extents, nullptr, true));
+    {
+      hb_glyf_scratch_t scratch;
+      return get_points (font, gid, points_aggregator_t (font, extents, nullptr, true),
+			 hb_array (font->coords, font->num_coords),
+			 scratch);
+    }
 #endif
 #endif
     return glyph_for_gid (gid).get_extents_without_var_scaled (font, *this, extents);
     return glyph_for_gid (gid).get_extents_without_var_scaled (font, *this, extents);
   }
   }
 
 
-  bool paint_glyph (hb_font_t *font, hb_codepoint_t gid, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const
-  {
-    funcs->push_clip_glyph (data, gid, font);
-    funcs->color (data, true, foreground);
-    funcs->pop_clip (data);
-
-    return true;
-  }
-
   const glyf_impl::Glyph
   const glyf_impl::Glyph
   glyph_for_gid (hb_codepoint_t gid, bool needs_padding_removal = false) const
   glyph_for_gid (hb_codepoint_t gid, bool needs_padding_removal = false) const
   {
   {
@@ -410,15 +475,52 @@ struct glyf_accelerator_t
 
 
   bool
   bool
   get_path (hb_font_t *font, hb_codepoint_t gid, hb_draw_session_t &draw_session) const
   get_path (hb_font_t *font, hb_codepoint_t gid, hb_draw_session_t &draw_session) const
-  { return get_points (font, gid, glyf_impl::path_builder_t (font, draw_session)); }
+  {
+    if (!has_data ()) return false;
+
+    hb_glyf_scratch_t *scratch;
+
+    // Borrow the cached strach buffer.
+    {
+      scratch = cached_scratch.get_acquire ();
+      if (!scratch || unlikely (!cached_scratch.cmpexch (scratch, nullptr)))
+      {
+	scratch = (hb_glyf_scratch_t *) hb_calloc (1, sizeof (hb_glyf_scratch_t));
+	if (unlikely (!scratch))
+	  return true;
+      }
+    }
+
+    bool ret = get_points (font, gid, glyf_impl::path_builder_t (font, draw_session),
+			   hb_array (font->coords, font->num_coords),
+			   *scratch);
+
+    // Put it back.
+    if (!cached_scratch.cmpexch (nullptr, scratch))
+    {
+      scratch->~hb_glyf_scratch_t ();
+      hb_free (scratch);
+    }
+
+    return ret;
+  }
 
 
   bool
   bool
   get_path_at (hb_font_t *font, hb_codepoint_t gid, hb_draw_session_t &draw_session,
   get_path_at (hb_font_t *font, hb_codepoint_t gid, hb_draw_session_t &draw_session,
-	       hb_array_t<const int> coords) const
-  { return get_points (font, gid, glyf_impl::path_builder_t (font, draw_session), coords); }
+	       hb_array_t<const int> coords,
+	       hb_glyf_scratch_t &scratch) const
+  {
+    if (!has_data ()) return false;
+    return get_points (font, gid, glyf_impl::path_builder_t (font, draw_session),
+		       coords,
+		       scratch);
+  }
 
 
 #ifndef HB_NO_VAR
 #ifndef HB_NO_VAR
   const gvar_accelerator_t *gvar;
   const gvar_accelerator_t *gvar;
+#ifndef HB_NO_BEYOND_64K
+  const GVAR_accelerator_t *GVAR;
+#endif
 #endif
 #endif
   const hmtx_accelerator_t *hmtx;
   const hmtx_accelerator_t *hmtx;
 #ifndef HB_NO_VERTICAL
 #ifndef HB_NO_VERTICAL
@@ -430,6 +532,7 @@ struct glyf_accelerator_t
   unsigned int num_glyphs;
   unsigned int num_glyphs;
   hb_blob_ptr_t<loca> loca_table;
   hb_blob_ptr_t<loca> loca_table;
   hb_blob_ptr_t<glyf> glyf_table;
   hb_blob_ptr_t<glyf> glyf_table;
+  hb_atomic_ptr_t<hb_glyf_scratch_t> cached_scratch;
 };
 };
 
 
 
 
@@ -439,7 +542,7 @@ glyf::_populate_subset_glyphs (const hb_subset_plan_t   *plan,
 			       hb_vector_t<glyf_impl::SubsetGlyph>& glyphs /* OUT */) const
 			       hb_vector_t<glyf_impl::SubsetGlyph>& glyphs /* OUT */) const
 {
 {
   OT::glyf_accelerator_t glyf (plan->source);
   OT::glyf_accelerator_t glyf (plan->source);
-  if (!glyphs.alloc (plan->new_to_old_gid_list.length, true)) return false;
+  if (!glyphs.alloc_exact (plan->new_to_old_gid_list.length)) return false;
 
 
   for (const auto &pair : plan->new_to_old_gid_list)
   for (const auto &pair : plan->new_to_old_gid_list)
   {
   {

+ 51 - 49
thirdparty/harfbuzz/src/OT/glyf/path-builder.hh

@@ -42,7 +42,7 @@ struct path_builder_t
   {
   {
     bool is_on_curve = point.flag & glyf_impl::SimpleGlyph::FLAG_ON_CURVE;
     bool is_on_curve = point.flag & glyf_impl::SimpleGlyph::FLAG_ON_CURVE;
 #ifdef HB_NO_CUBIC_GLYF
 #ifdef HB_NO_CUBIC_GLYF
-    bool is_cubic = false;
+    constexpr bool is_cubic = false;
 #else
 #else
     bool is_cubic = !is_on_curve && (point.flag & glyf_impl::SimpleGlyph::FLAG_CUBIC);
     bool is_cubic = !is_on_curve && (point.flag & glyf_impl::SimpleGlyph::FLAG_CUBIC);
 #endif
 #endif
@@ -124,58 +124,60 @@ struct path_builder_t
       }
       }
     }
     }
 
 
-    if (unlikely (point.is_end_point))
-    {
-      if (first_offcurve && last_offcurve)
-      {
-	optional_point_t mid = last_offcurve.mid (first_offcurve2 ?
-						  first_offcurve2 :
-						  first_offcurve);
-	if (last_offcurve2)
-	  draw_session->cubic_to (last_offcurve2.x, last_offcurve2.y,
-				  last_offcurve.x, last_offcurve.y,
-				  mid.x, mid.y);
-	else
-	  draw_session->quadratic_to (last_offcurve.x, last_offcurve.y,
-				     mid.x, mid.y);
-	last_offcurve = optional_point_t ();
-      }
-      /* now check the rest */
+  }
 
 
-      if (first_offcurve && first_oncurve)
-      {
-        if (first_offcurve2)
-	  draw_session->cubic_to (first_offcurve2.x, first_offcurve2.y,
-				  first_offcurve.x, first_offcurve.y,
-				  first_oncurve.x, first_oncurve.y);
-	else
-	  draw_session->quadratic_to (first_offcurve.x, first_offcurve.y,
-				     first_oncurve.x, first_oncurve.y);
-      }
-      else if (last_offcurve && first_oncurve)
-      {
-	if (last_offcurve2)
-	  draw_session->cubic_to (last_offcurve2.x, last_offcurve2.y,
-				  last_offcurve.x, last_offcurve.y,
-				  first_oncurve.x, first_oncurve.y);
-	else
-	  draw_session->quadratic_to (last_offcurve.x, last_offcurve.y,
-				     first_oncurve.x, first_oncurve.y);
-      }
-      else if (first_oncurve)
-	draw_session->line_to (first_oncurve.x, first_oncurve.y);
-      else if (first_offcurve)
-      {
-	float x = first_offcurve.x, y = first_offcurve.y;
-	draw_session->move_to (x, y);
-	draw_session->quadratic_to (x, y, x, y);
-      }
+  void contour_end ()
+  {
+    if (first_offcurve && last_offcurve)
+    {
+      optional_point_t mid = last_offcurve.mid (first_offcurve2 ?
+						first_offcurve2 :
+						first_offcurve);
+      if (last_offcurve2)
+	draw_session->cubic_to (last_offcurve2.x, last_offcurve2.y,
+				last_offcurve.x, last_offcurve.y,
+				mid.x, mid.y);
+      else
+	draw_session->quadratic_to (last_offcurve.x, last_offcurve.y,
+				   mid.x, mid.y);
+      last_offcurve = optional_point_t ();
+    }
+    /* now check the rest */
 
 
-      /* Getting ready for the next contour */
-      first_oncurve = first_offcurve = last_offcurve = last_offcurve2 = optional_point_t ();
-      draw_session->close_path ();
+    if (first_offcurve && first_oncurve)
+    {
+      if (first_offcurve2)
+	draw_session->cubic_to (first_offcurve2.x, first_offcurve2.y,
+				first_offcurve.x, first_offcurve.y,
+				first_oncurve.x, first_oncurve.y);
+      else
+	draw_session->quadratic_to (first_offcurve.x, first_offcurve.y,
+				   first_oncurve.x, first_oncurve.y);
     }
     }
+    else if (last_offcurve && first_oncurve)
+    {
+      if (last_offcurve2)
+	draw_session->cubic_to (last_offcurve2.x, last_offcurve2.y,
+				last_offcurve.x, last_offcurve.y,
+				first_oncurve.x, first_oncurve.y);
+      else
+	draw_session->quadratic_to (last_offcurve.x, last_offcurve.y,
+				   first_oncurve.x, first_oncurve.y);
+    }
+    else if (first_oncurve)
+      draw_session->line_to (first_oncurve.x, first_oncurve.y);
+    else if (first_offcurve)
+    {
+      float x = first_offcurve.x, y = first_offcurve.y;
+      draw_session->move_to (x, y);
+      draw_session->quadratic_to (x, y, x, y);
+    }
+
+    /* Getting ready for the next contour */
+    first_oncurve = first_offcurve = last_offcurve = last_offcurve2 = optional_point_t ();
+    draw_session->close_path ();
   }
   }
+
   void points_end () {}
   void points_end () {}
 
 
   bool is_consuming_contour_points () { return true; }
   bool is_consuming_contour_points () { return true; }

+ 5 - 5
thirdparty/harfbuzz/src/OT/name/name.hh

@@ -163,7 +163,7 @@ struct NameRecord
       if (platformID != 1)
       if (platformID != 1)
       {
       {
         unsigned text_size = hb_ot_name_convert_utf<hb_utf8_t, hb_utf16_be_t> (*name_bytes, nullptr, nullptr);
         unsigned text_size = hb_ot_name_convert_utf<hb_utf8_t, hb_utf16_be_t> (*name_bytes, nullptr, nullptr);
-  
+
         text_size++; // needs to consider NULL terminator for use in hb_ot_name_convert_utf()
         text_size++; // needs to consider NULL terminator for use in hb_ot_name_convert_utf()
         unsigned byte_len = text_size * hb_utf16_be_t::codepoint_t::static_size;
         unsigned byte_len = text_size * hb_utf16_be_t::codepoint_t::static_size;
         name_str_utf16_be = (char *) hb_calloc (byte_len, 1);
         name_str_utf16_be = (char *) hb_calloc (byte_len, 1);
@@ -174,14 +174,14 @@ struct NameRecord
         }
         }
         hb_ot_name_convert_utf<hb_utf8_t, hb_utf16_be_t> (*name_bytes, &text_size,
         hb_ot_name_convert_utf<hb_utf8_t, hb_utf16_be_t> (*name_bytes, &text_size,
                                                           (hb_utf16_be_t::codepoint_t *) name_str_utf16_be);
                                                           (hb_utf16_be_t::codepoint_t *) name_str_utf16_be);
-  
+
         unsigned encoded_byte_len = text_size * hb_utf16_be_t::codepoint_t::static_size;
         unsigned encoded_byte_len = text_size * hb_utf16_be_t::codepoint_t::static_size;
         if (!encoded_byte_len || !c->check_assign (out->length, encoded_byte_len, HB_SERIALIZE_ERROR_INT_OVERFLOW)) {
         if (!encoded_byte_len || !c->check_assign (out->length, encoded_byte_len, HB_SERIALIZE_ERROR_INT_OVERFLOW)) {
           c->revert (snap);
           c->revert (snap);
           hb_free (name_str_utf16_be);
           hb_free (name_str_utf16_be);
           return_trace (nullptr);
           return_trace (nullptr);
         }
         }
-  
+
         encoded_bytes = hb_bytes_t (name_str_utf16_be, encoded_byte_len);
         encoded_bytes = hb_bytes_t (name_str_utf16_be, encoded_byte_len);
       }
       }
       else
       else
@@ -392,7 +392,7 @@ struct name
     const hb_hashmap_t<hb_ot_name_record_ids_t, hb_bytes_t> *name_table_overrides =
     const hb_hashmap_t<hb_ot_name_record_ids_t, hb_bytes_t> *name_table_overrides =
         &c->plan->name_table_overrides;
         &c->plan->name_table_overrides;
 #endif
 #endif
-    
+
     auto it =
     auto it =
     + nameRecordZ.as_array (count)
     + nameRecordZ.as_array (count)
     | hb_filter (c->plan->name_ids, &NameRecord::nameID)
     | hb_filter (c->plan->name_ids, &NameRecord::nameID)
@@ -485,7 +485,7 @@ struct name
       const hb_array_t<const NameRecord> all_names (this->table->nameRecordZ.arrayZ,
       const hb_array_t<const NameRecord> all_names (this->table->nameRecordZ.arrayZ,
 						    this->table->count);
 						    this->table->count);
 
 
-      this->names.alloc (all_names.length, true);
+      this->names.alloc_exact (all_names.length);
 
 
       for (unsigned int i = 0; i < all_names.length; i++)
       for (unsigned int i = 0; i < all_names.length; i++)
       {
       {

+ 178 - 65
thirdparty/harfbuzz/src/hb-aat-layout-common.hh

@@ -30,6 +30,10 @@
 #include "hb-aat-layout.hh"
 #include "hb-aat-layout.hh"
 #include "hb-aat-map.hh"
 #include "hb-aat-map.hh"
 #include "hb-open-type.hh"
 #include "hb-open-type.hh"
+#include "hb-cache.hh"
+#include "hb-bit-set.hh"
+#include "hb-bit-page.hh"
+
 
 
 namespace OT {
 namespace OT {
 struct GDEF;
 struct GDEF;
@@ -39,10 +43,11 @@ namespace AAT {
 
 
 using namespace OT;
 using namespace OT;
 
 
-#define HB_AAT_BUFFER_DIGEST_THRESHOLD 32
-
 struct ankr;
 struct ankr;
 
 
+using hb_aat_class_cache_t = hb_cache_t<15, 8, 7>;
+static_assert (sizeof (hb_aat_class_cache_t) == 256, "");
+
 struct hb_aat_apply_context_t :
 struct hb_aat_apply_context_t :
        hb_dispatch_context_t<hb_aat_apply_context_t, bool, HB_DEBUG_APPLY>
        hb_dispatch_context_t<hb_aat_apply_context_t, bool, HB_DEBUG_APPLY>
 {
 {
@@ -61,10 +66,12 @@ struct hb_aat_apply_context_t :
   const ankr *ankr_table;
   const ankr *ankr_table;
   const OT::GDEF *gdef_table;
   const OT::GDEF *gdef_table;
   const hb_sorted_vector_t<hb_aat_map_t::range_flags_t> *range_flags = nullptr;
   const hb_sorted_vector_t<hb_aat_map_t::range_flags_t> *range_flags = nullptr;
-  hb_set_digest_t buffer_digest = hb_set_digest_t::full ();
-  hb_set_digest_t machine_glyph_set = hb_set_digest_t::full ();
-  hb_set_digest_t left_set = hb_set_digest_t::full ();
-  hb_set_digest_t right_set = hb_set_digest_t::full ();
+  bool using_buffer_glyph_set = false;
+  hb_bit_set_t buffer_glyph_set;
+  const hb_bit_set_t *left_set = nullptr;
+  const hb_bit_set_t *right_set = nullptr;
+  const hb_bit_set_t *machine_glyph_set = nullptr;
+  hb_aat_class_cache_t *machine_class_cache = nullptr;
   hb_mask_t subtable_flags = 0;
   hb_mask_t subtable_flags = 0;
 
 
   /* Unused. For debug tracing only. */
   /* Unused. For debug tracing only. */
@@ -80,6 +87,25 @@ struct hb_aat_apply_context_t :
   HB_INTERNAL void set_ankr_table (const AAT::ankr *ankr_table_);
   HB_INTERNAL void set_ankr_table (const AAT::ankr *ankr_table_);
 
 
   void set_lookup_index (unsigned int i) { lookup_index = i; }
   void set_lookup_index (unsigned int i) { lookup_index = i; }
+
+  void setup_buffer_glyph_set ()
+  {
+    using_buffer_glyph_set = buffer->len >= 4;
+
+    if (using_buffer_glyph_set)
+      buffer->collect_codepoints (buffer_glyph_set);
+  }
+  bool buffer_intersects_machine () const
+  {
+    if (using_buffer_glyph_set)
+      return buffer_glyph_set.intersects (*machine_glyph_set);
+
+    // Faster for shorter buffers.
+    for (unsigned i = 0; i < buffer->len; i++)
+      if (machine_glyph_set->has (buffer->info[i].codepoint))
+	return true;
+    return false;
+  }
 };
 };
 
 
 
 
@@ -108,6 +134,13 @@ struct LookupFormat0
   {
   {
     glyphs.add_range (0, num_glyphs - 1);
     glyphs.add_range (0, num_glyphs - 1);
   }
   }
+  template <typename set_t, typename filter_t>
+  void collect_glyphs_filtered (set_t &glyphs, unsigned num_glyphs, const filter_t &filter) const
+  {
+    for (unsigned i = 0; i < num_glyphs; i++)
+      if (filter (arrayZ[i]))
+	glyphs.add (i);
+  }
 
 
   bool sanitize (hb_sanitize_context_t *c) const
   bool sanitize (hb_sanitize_context_t *c) const
   {
   {
@@ -140,8 +173,13 @@ struct LookupSegmentSingle
   template <typename set_t>
   template <typename set_t>
   void collect_glyphs (set_t &glyphs) const
   void collect_glyphs (set_t &glyphs) const
   {
   {
-    if (first == DELETED_GLYPH)
-      return;
+    if (first == DELETED_GLYPH) return;
+    glyphs.add_range (first, last);
+  }
+  template <typename set_t, typename filter_t>
+  void collect_glyphs_filtered (set_t &glyphs, const filter_t &filter) const
+  {
+    if (!filter (value)) return;
     glyphs.add_range (first, last);
     glyphs.add_range (first, last);
   }
   }
 
 
@@ -182,6 +220,13 @@ struct LookupFormat2
     for (unsigned int i = 0; i < count; i++)
     for (unsigned int i = 0; i < count; i++)
       segments[i].collect_glyphs (glyphs);
       segments[i].collect_glyphs (glyphs);
   }
   }
+  template <typename set_t, typename filter_t>
+  void collect_glyphs_filtered (set_t &glyphs, const filter_t &filter) const
+  {
+    unsigned count = segments.get_length ();
+    for (unsigned int i = 0; i < count; i++)
+      segments[i].collect_glyphs_filtered (glyphs, filter);
+  }
 
 
   bool sanitize (hb_sanitize_context_t *c) const
   bool sanitize (hb_sanitize_context_t *c) const
   {
   {
@@ -217,10 +262,17 @@ struct LookupSegmentArray
   template <typename set_t>
   template <typename set_t>
   void collect_glyphs (set_t &glyphs) const
   void collect_glyphs (set_t &glyphs) const
   {
   {
-    if (first == DELETED_GLYPH)
-      return;
+    if (first == DELETED_GLYPH) return;
     glyphs.add_range (first, last);
     glyphs.add_range (first, last);
   }
   }
+  template <typename set_t, typename filter_t>
+  void collect_glyphs_filtered (set_t &glyphs, const void *base, const filter_t &filter) const
+  {
+    const auto &values = base+valuesZ;
+    for (hb_codepoint_t i = first; i <= last; i++)
+      if (filter (values[i - first]))
+	glyphs.add (i);
+  }
 
 
   int cmp (hb_codepoint_t g) const
   int cmp (hb_codepoint_t g) const
   { return g < first ? -1 : g <= last ? 0 : +1; }
   { return g < first ? -1 : g <= last ? 0 : +1; }
@@ -271,6 +323,13 @@ struct LookupFormat4
     for (unsigned i = 0; i < count; i++)
     for (unsigned i = 0; i < count; i++)
       segments[i].collect_glyphs (glyphs);
       segments[i].collect_glyphs (glyphs);
   }
   }
+  template <typename set_t, typename filter_t>
+  void collect_glyphs_filtered (set_t &glyphs, const filter_t &filter) const
+  {
+    unsigned count = segments.get_length ();
+    for (unsigned i = 0; i < count; i++)
+      segments[i].collect_glyphs_filtered (glyphs, this, filter);
+  }
 
 
   bool sanitize (hb_sanitize_context_t *c) const
   bool sanitize (hb_sanitize_context_t *c) const
   {
   {
@@ -303,8 +362,13 @@ struct LookupSingle
   template <typename set_t>
   template <typename set_t>
   void collect_glyphs (set_t &glyphs) const
   void collect_glyphs (set_t &glyphs) const
   {
   {
-    if (glyph == DELETED_GLYPH)
-      return;
+    if (glyph == DELETED_GLYPH) return;
+    glyphs.add (glyph);
+  }
+  template <typename set_t, typename filter_t>
+  void collect_glyphs_filtered (set_t &glyphs, const filter_t &filter) const
+  {
+    if (!filter (value)) return;
     glyphs.add (glyph);
     glyphs.add (glyph);
   }
   }
 
 
@@ -344,6 +408,13 @@ struct LookupFormat6
     for (unsigned i = 0; i < count; i++)
     for (unsigned i = 0; i < count; i++)
       entries[i].collect_glyphs (glyphs);
       entries[i].collect_glyphs (glyphs);
   }
   }
+  template <typename set_t, typename filter_t>
+  void collect_glyphs_filtered (set_t &glyphs, const filter_t &filter) const
+  {
+    unsigned count = entries.get_length ();
+    for (unsigned i = 0; i < count; i++)
+      entries[i].collect_glyphs_filtered (glyphs, filter);
+  }
 
 
   bool sanitize (hb_sanitize_context_t *c) const
   bool sanitize (hb_sanitize_context_t *c) const
   {
   {
@@ -379,12 +450,20 @@ struct LookupFormat8
   template <typename set_t>
   template <typename set_t>
   void collect_glyphs (set_t &glyphs) const
   void collect_glyphs (set_t &glyphs) const
   {
   {
-    if (unlikely (!glyphCount))
-      return;
-    if (firstGlyph == DELETED_GLYPH)
-      return;
+    if (unlikely (!glyphCount)) return;
+    if (firstGlyph == DELETED_GLYPH) return;
     glyphs.add_range (firstGlyph, firstGlyph + glyphCount - 1);
     glyphs.add_range (firstGlyph, firstGlyph + glyphCount - 1);
   }
   }
+  template <typename set_t, typename filter_t>
+  void collect_glyphs_filtered (set_t &glyphs, const filter_t &filter) const
+  {
+    if (unlikely (!glyphCount)) return;
+    if (firstGlyph == DELETED_GLYPH) return;
+    const T *p = valueArrayZ.arrayZ;
+    for (unsigned i = 0; i < glyphCount; i++)
+      if (filter (p[i]))
+	glyphs.add (firstGlyph + i);
+  }
 
 
   bool sanitize (hb_sanitize_context_t *c) const
   bool sanitize (hb_sanitize_context_t *c) const
   {
   {
@@ -433,10 +512,8 @@ struct LookupFormat10
   template <typename set_t>
   template <typename set_t>
   void collect_glyphs (set_t &glyphs) const
   void collect_glyphs (set_t &glyphs) const
   {
   {
-    if (unlikely (!glyphCount))
-      return;
-    if (firstGlyph == DELETED_GLYPH)
-      return;
+    if (unlikely (!glyphCount)) return;
+    if (firstGlyph == DELETED_GLYPH) return;
     glyphs.add_range (firstGlyph, firstGlyph + glyphCount - 1);
     glyphs.add_range (firstGlyph, firstGlyph + glyphCount - 1);
   }
   }
 
 
@@ -501,6 +578,18 @@ struct Lookup
     default:return;
     default:return;
     }
     }
   }
   }
+  template <typename set_t, typename filter_t>
+  void collect_glyphs_filtered (set_t &glyphs, unsigned num_glyphs, const filter_t &filter) const
+  {
+    switch (u.format) {
+    case 0: hb_barrier (); u.format0.collect_glyphs_filtered (glyphs, num_glyphs, filter); return;
+    case 2: hb_barrier (); u.format2.collect_glyphs_filtered (glyphs, filter); return;
+    case 4: hb_barrier (); u.format4.collect_glyphs_filtered (glyphs, filter); return;
+    case 6: hb_barrier (); u.format6.collect_glyphs_filtered (glyphs, filter); return;
+    case 8: hb_barrier (); u.format8.collect_glyphs_filtered (glyphs, filter); return;
+    default:return;
+    }
+  }
 
 
   typename T::type get_class (hb_codepoint_t glyph_id,
   typename T::type get_class (hb_codepoint_t glyph_id,
 			      unsigned int num_glyphs,
 			      unsigned int num_glyphs,
@@ -563,7 +652,7 @@ DECLARE_NULL_NAMESPACE_BYTES_TEMPLATE1 (AAT, Lookup, 2);
 template <typename T>
 template <typename T>
 struct Entry
 struct Entry
 {
 {
-  // This does seem like it's ever called.
+  // This doesn't seem like it's ever called.
   bool sanitize (hb_sanitize_context_t *c) const
   bool sanitize (hb_sanitize_context_t *c) const
   {
   {
     TRACE_SANITIZE (this);
     TRACE_SANITIZE (this);
@@ -632,18 +721,47 @@ struct StateTable
   {
   {
     (this+classTable).collect_glyphs (glyphs, num_glyphs);
     (this+classTable).collect_glyphs (glyphs, num_glyphs);
   }
   }
+  template <typename set_t, typename table_t>
+  void collect_initial_glyphs (set_t &glyphs, unsigned num_glyphs, const table_t &table) const
+  {
+    unsigned num_classes = nClasses;
+
+    if (unlikely (num_classes > hb_bit_page_t::BITS))
+    {
+      (this+classTable).collect_glyphs (glyphs, num_glyphs);
+      return;
+    }
+
+    // Collect all classes going out from the start state.
+    hb_bit_page_t filter;
+
+    for (unsigned i = 0; i < num_classes; i++)
+    {
+      const auto &entry = get_entry (STATE_START_OF_TEXT, i);
+      if (new_state (entry.newState) == STATE_START_OF_TEXT &&
+	  !table.is_action_initiable (entry) && !table.is_actionable (entry))
+	continue;
+
+      filter.add (i);
+    }
+
+    // And glyphs in those classes.
+    (this+classTable).collect_glyphs_filtered (glyphs, num_glyphs, filter);
+  }
 
 
   int new_state (unsigned int newState) const
   int new_state (unsigned int newState) const
   { return Types::extended ? newState : ((int) newState - (int) stateArrayTable) / (int) nClasses; }
   { return Types::extended ? newState : ((int) newState - (int) stateArrayTable) / (int) nClasses; }
 
 
-  template <typename set_t>
   unsigned int get_class (hb_codepoint_t glyph_id,
   unsigned int get_class (hb_codepoint_t glyph_id,
 			  unsigned int num_glyphs,
 			  unsigned int num_glyphs,
-			  const set_t &glyphs) const
+			  hb_aat_class_cache_t *cache = nullptr) const
   {
   {
+    unsigned klass;
+    if (cache && cache->get (glyph_id, &klass)) return klass;
     if (unlikely (glyph_id == DELETED_GLYPH)) return CLASS_DELETED_GLYPH;
     if (unlikely (glyph_id == DELETED_GLYPH)) return CLASS_DELETED_GLYPH;
-    if (!glyphs[glyph_id]) return CLASS_OUT_OF_BOUNDS;
-    return (this+classTable).get_class (glyph_id, num_glyphs, CLASS_OUT_OF_BOUNDS);
+    klass = (this+classTable).get_class (glyph_id, num_glyphs, CLASS_OUT_OF_BOUNDS);
+    if (cache) cache->set (glyph_id, klass);
+    return klass;
   }
   }
 
 
   const Entry<Extra> *get_entries () const
   const Entry<Extra> *get_entries () const
@@ -651,13 +769,14 @@ struct StateTable
 
 
   const Entry<Extra> &get_entry (int state, unsigned int klass) const
   const Entry<Extra> &get_entry (int state, unsigned int klass) const
   {
   {
-    if (unlikely (klass >= nClasses))
+    unsigned n_classes = nClasses;
+    if (unlikely (klass >= n_classes))
       klass = CLASS_OUT_OF_BOUNDS;
       klass = CLASS_OUT_OF_BOUNDS;
 
 
     const HBUSHORT *states = (this+stateArrayTable).arrayZ;
     const HBUSHORT *states = (this+stateArrayTable).arrayZ;
     const Entry<Extra> *entries = (this+entryTable).arrayZ;
     const Entry<Extra> *entries = (this+entryTable).arrayZ;
 
 
-    unsigned int entry = states[state * nClasses + klass];
+    unsigned int entry = states[state * n_classes + klass];
     DEBUG_MSG (APPLY, nullptr, "e%u", entry);
     DEBUG_MSG (APPLY, nullptr, "e%u", entry);
 
 
     return entries[entry];
     return entries[entry];
@@ -803,6 +922,13 @@ struct ClassTable
       if (classArray.arrayZ[i] != CLASS_OUT_OF_BOUNDS)
       if (classArray.arrayZ[i] != CLASS_OUT_OF_BOUNDS)
 	glyphs.add (firstGlyph + i);
 	glyphs.add (firstGlyph + i);
   }
   }
+  template <typename set_t, typename filter_t>
+  void collect_glyphs_filtered (set_t &glyphs, unsigned num_glyphs, const filter_t &filter) const
+  {
+    for (unsigned i = 0; i < classArray.len; i++)
+      if (filter (classArray.arrayZ[i]))
+	glyphs.add (firstGlyph + i);
+  }
 
 
   bool sanitize (hb_sanitize_context_t *c) const
   bool sanitize (hb_sanitize_context_t *c) const
   {
   {
@@ -918,7 +1044,7 @@ struct ExtendedTypes
   }
   }
 };
 };
 
 
-template <typename Types, typename EntryData>
+template <typename Types, typename EntryData, typename Flags>
 struct StateTableDriver
 struct StateTableDriver
 {
 {
   using StateTableT = StateTable<Types, EntryData>;
   using StateTableT = StateTable<Types, EntryData>;
@@ -929,14 +1055,6 @@ struct StateTableDriver
 	      machine (machine_),
 	      machine (machine_),
 	      num_glyphs (face_->get_num_glyphs ()) {}
 	      num_glyphs (face_->get_num_glyphs ()) {}
 
 
-  template <typename context_t>
-  bool is_idempotent_on_all_out_of_bounds (context_t *c, hb_aat_apply_context_t *ac)
-  {
-    const auto entry = machine.get_entry (StateTableT::STATE_START_OF_TEXT, CLASS_OUT_OF_BOUNDS);
-    return !c->is_actionable (ac->buffer, this, entry) &&
-	    machine.new_state (entry.newState) == StateTableT::STATE_START_OF_TEXT;
-  }
-
   template <typename context_t>
   template <typename context_t>
   void drive (context_t *c, hb_aat_apply_context_t *ac)
   void drive (context_t *c, hb_aat_apply_context_t *ac)
   {
   {
@@ -977,7 +1095,7 @@ struct StateTableDriver
       }
       }
 
 
       unsigned int klass = likely (buffer->idx < buffer->len) ?
       unsigned int klass = likely (buffer->idx < buffer->len) ?
-			   machine.get_class (buffer->cur().codepoint, num_glyphs, ac->machine_glyph_set) :
+			   machine.get_class (buffer->cur().codepoint, num_glyphs, ac->machine_class_cache) :
 			   (unsigned) CLASS_END_OF_TEXT;
 			   (unsigned) CLASS_END_OF_TEXT;
       DEBUG_MSG (APPLY, nullptr, "c%u at %u", klass, buffer->idx);
       DEBUG_MSG (APPLY, nullptr, "c%u at %u", klass, buffer->idx);
       const EntryT &entry = machine.get_entry (state, klass);
       const EntryT &entry = machine.get_entry (state, klass);
@@ -1011,41 +1129,36 @@ struct StateTableDriver
        *
        *
        *   https://github.com/harfbuzz/harfbuzz/issues/2860
        *   https://github.com/harfbuzz/harfbuzz/issues/2860
        */
        */
-
-      const auto is_safe_to_break_extra = [&]()
-      {
-          /* 2c. */
-          const auto &wouldbe_entry = machine.get_entry(StateTableT::STATE_START_OF_TEXT, klass);
-
-          /* 2c'. */
-          if (c->is_actionable (buffer, this, wouldbe_entry))
-	    return false;
-
-          /* 2c". */
-          return next_state == machine.new_state(wouldbe_entry.newState)
-              && (entry.flags & context_t::DontAdvance) == (wouldbe_entry.flags & context_t::DontAdvance);
-      };
-
-      const auto is_safe_to_break = [&]()
-      {
+      const EntryT *wouldbe_entry;
+      bool is_safe_to_break =
+      (
           /* 1. */
           /* 1. */
-          if (c->is_actionable (buffer, this, entry))
-              return false;
+          !c->table->is_actionable (entry) &&
 
 
           /* 2. */
           /* 2. */
           // This one is meh, I know...
           // This one is meh, I know...
-          const auto ok =
+	  (
                  state == StateTableT::STATE_START_OF_TEXT
                  state == StateTableT::STATE_START_OF_TEXT
-              || ((entry.flags & context_t::DontAdvance) && next_state == StateTableT::STATE_START_OF_TEXT)
-              || is_safe_to_break_extra();
-          if (!ok)
-              return false;
+              || ((entry.flags & Flags::DontAdvance) && next_state == StateTableT::STATE_START_OF_TEXT)
+              || (
+		    /* 2c. */
+		    wouldbe_entry = &machine.get_entry(StateTableT::STATE_START_OF_TEXT, klass)
+		    ,
+		    /* 2c'. */
+		    !c->table->is_actionable (*wouldbe_entry) &&
+		    /* 2c". */
+		    (
+		      next_state == machine.new_state(wouldbe_entry->newState) &&
+		      (entry.flags & Flags::DontAdvance) == (wouldbe_entry->flags & Flags::DontAdvance)
+		    )
+		 )
+	  ) &&
 
 
           /* 3. */
           /* 3. */
-          return !c->is_actionable (buffer, this, machine.get_entry (state, CLASS_END_OF_TEXT));
-      };
+          !c->table->is_actionable (machine.get_entry (state, CLASS_END_OF_TEXT))
+      );
 
 
-      if (!is_safe_to_break () && buffer->backtrack_len () && buffer->idx < buffer->len)
+      if (!is_safe_to_break && buffer->backtrack_len () && buffer->idx < buffer->len)
 	buffer->unsafe_to_break_from_outbuffer (buffer->backtrack_len () - 1, buffer->idx + 1);
 	buffer->unsafe_to_break_from_outbuffer (buffer->backtrack_len () - 1, buffer->idx + 1);
 
 
       c->transition (buffer, this, entry);
       c->transition (buffer, this, entry);
@@ -1056,7 +1169,7 @@ struct StateTableDriver
       if (buffer->idx == buffer->len || unlikely (!buffer->successful))
       if (buffer->idx == buffer->len || unlikely (!buffer->successful))
 	break;
 	break;
 
 
-      if (!(entry.flags & context_t::DontAdvance) || buffer->max_ops-- <= 0)
+      if (!(entry.flags & Flags::DontAdvance) || buffer->max_ops-- <= 0)
 	(void) buffer->next_glyph ();
 	(void) buffer->next_glyph ();
     }
     }
 
 

+ 54 - 70
thirdparty/harfbuzz/src/hb-aat-layout-kerx-table.hh

@@ -112,10 +112,6 @@ struct KerxSubTableFormat0
     if (header.coverage & header.Backwards)
     if (header.coverage & header.Backwards)
       return_trace (false);
       return_trace (false);
 
 
-    if (!(c->buffer_digest.may_have (c->left_set) &&
-	  c->buffer_digest.may_have (c->right_set)))
-      return_trace (false);
-
     accelerator_t accel (*this, c);
     accelerator_t accel (*this, c);
     hb_kern_machine_t<accelerator_t> machine (accel, header.coverage & header.CrossStream);
     hb_kern_machine_t<accelerator_t> machine (accel, header.coverage & header.CrossStream);
     machine.kern (c->font, c->buffer, c->plan->kern_mask);
     machine.kern (c->font, c->buffer, c->plan->kern_mask);
@@ -144,7 +140,7 @@ struct KerxSubTableFormat0
 
 
     int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
     int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
     {
     {
-      if (!c->left_set[left] || !c->right_set[right]) return 0;
+      if (!(*c->left_set)[left] || !(*c->right_set)[right]) return 0;
       return table.get_kerning (left, right, c);
       return table.get_kerning (left, right, c);
     }
     }
   };
   };
@@ -211,6 +207,9 @@ struct Format1Entry<false>
 
 
   typedef void EntryData;
   typedef void EntryData;
 
 
+  static bool initiateAction (const Entry<EntryData> &entry)
+  { return entry.flags & Push; }
+
   static bool performAction (const Entry<EntryData> &entry)
   static bool performAction (const Entry<EntryData> &entry)
   { return entry.flags & Offset; }
   { return entry.flags & Offset; }
 
 
@@ -227,13 +226,23 @@ struct KerxSubTableFormat1
   typedef Format1Entry<Types::extended> Format1EntryT;
   typedef Format1Entry<Types::extended> Format1EntryT;
   typedef typename Format1EntryT::EntryData EntryData;
   typedef typename Format1EntryT::EntryData EntryData;
 
 
+  enum Flags
+  {
+    DontAdvance	= Format1EntryT::DontAdvance,
+  };
+
+  bool is_action_initiable (const Entry<EntryData> &entry) const
+  {
+    return Format1EntryT::initiateAction (entry);
+  }
+  bool is_actionable (const Entry<EntryData> &entry) const
+  {
+    return Format1EntryT::performAction (entry);
+  }
+
   struct driver_context_t
   struct driver_context_t
   {
   {
     static constexpr bool in_place = true;
     static constexpr bool in_place = true;
-    enum
-    {
-      DontAdvance	= Format1EntryT::DontAdvance,
-    };
 
 
     driver_context_t (const KerxSubTableFormat1 *table_,
     driver_context_t (const KerxSubTableFormat1 *table_,
 		      hb_aat_apply_context_t *c_) :
 		      hb_aat_apply_context_t *c_) :
@@ -246,12 +255,8 @@ struct KerxSubTableFormat1
 	depth (0),
 	depth (0),
 	crossStream (table->header.coverage & table->header.CrossStream) {}
 	crossStream (table->header.coverage & table->header.CrossStream) {}
 
 
-    bool is_actionable (hb_buffer_t *buffer HB_UNUSED,
-			StateTableDriver<Types, EntryData> *driver HB_UNUSED,
-			const Entry<EntryData> &entry)
-    { return Format1EntryT::performAction (entry); }
     void transition (hb_buffer_t *buffer,
     void transition (hb_buffer_t *buffer,
-		     StateTableDriver<Types, EntryData> *driver,
+		     StateTableDriver<Types, EntryData, Flags> *driver,
 		     const Entry<EntryData> &entry)
 		     const Entry<EntryData> &entry)
     {
     {
       unsigned int flags = entry.flags;
       unsigned int flags = entry.flags;
@@ -351,9 +356,10 @@ struct KerxSubTableFormat1
       }
       }
     }
     }
 
 
-    private:
+    public:
     hb_aat_apply_context_t *c;
     hb_aat_apply_context_t *c;
     const KerxSubTableFormat1 *table;
     const KerxSubTableFormat1 *table;
+    private:
     const UnsizedArrayOf<FWORD> &kernAction;
     const UnsizedArrayOf<FWORD> &kernAction;
     unsigned int stack[8];
     unsigned int stack[8];
     unsigned int depth;
     unsigned int depth;
@@ -370,12 +376,7 @@ struct KerxSubTableFormat1
 
 
     driver_context_t dc (this, c);
     driver_context_t dc (this, c);
 
 
-    StateTableDriver<Types, EntryData> driver (machine, c->font->face);
-
-    if (driver.is_idempotent_on_all_out_of_bounds (&dc, c) &&
-	!(c->buffer_digest.may_have (c->left_set) &&
-	  c->buffer_digest.may_have (c->right_set)))
-      return_trace (false);
+    StateTableDriver<Types, EntryData, Flags> driver (machine, c->font->face);
 
 
     driver.drive (&dc, c);
     driver.drive (&dc, c);
 
 
@@ -440,10 +441,6 @@ struct KerxSubTableFormat2
     if (header.coverage & header.Backwards)
     if (header.coverage & header.Backwards)
       return_trace (false);
       return_trace (false);
 
 
-    if (!(c->buffer_digest.may_have (c->left_set) &&
-	  c->buffer_digest.may_have (c->right_set)))
-      return_trace (false);
-
     accelerator_t accel (*this, c);
     accelerator_t accel (*this, c);
     hb_kern_machine_t<accelerator_t> machine (accel, header.coverage & header.CrossStream);
     hb_kern_machine_t<accelerator_t> machine (accel, header.coverage & header.CrossStream);
     machine.kern (c->font, c->buffer, c->plan->kern_mask);
     machine.kern (c->font, c->buffer, c->plan->kern_mask);
@@ -469,7 +466,7 @@ struct KerxSubTableFormat2
 
 
     int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
     int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
     {
     {
-      if (!c->left_set[left] || !c->right_set[right]) return 0;
+      if (!(*c->left_set)[left] || !(*c->right_set)[right]) return 0;
       return table.get_kerning (left, right, c);
       return table.get_kerning (left, right, c);
     }
     }
   };
   };
@@ -513,17 +510,26 @@ struct KerxSubTableFormat4
     DEFINE_SIZE_STATIC (2);
     DEFINE_SIZE_STATIC (2);
   };
   };
 
 
-  struct driver_context_t
+  enum Flags
   {
   {
-    static constexpr bool in_place = true;
-    enum Flags
-    {
-      Mark		= 0x8000,	/* If set, remember this glyph as the marked glyph. */
-      DontAdvance	= 0x4000,	/* If set, don't advance to the next glyph before
+    Mark		= 0x8000,	/* If set, remember this glyph as the marked glyph. */
+    DontAdvance		= 0x4000,	/* If set, don't advance to the next glyph before
 					 * going to the new state. */
 					 * going to the new state. */
-      Reserved		= 0x3FFF,	/* Not used; set to 0. */
-    };
+    Reserved		= 0x3FFF,	/* Not used; set to 0. */
+  };
+
+  bool is_action_initiable (const Entry<EntryData> &entry) const
+  {
+    return (entry.flags & Mark);
+  }
+  bool is_actionable (const Entry<EntryData> &entry) const
+  {
+    return entry.data.ankrActionIndex != 0xFFFF;
+  }
 
 
+  struct driver_context_t
+  {
+    static constexpr bool in_place = true;
     enum SubTableFlags
     enum SubTableFlags
     {
     {
       ActionType	= 0xC0000000,	/* A two-bit field containing the action type. */
       ActionType	= 0xC0000000,	/* A two-bit field containing the action type. */
@@ -533,20 +539,17 @@ struct KerxSubTableFormat4
 					 * point table. */
 					 * point table. */
     };
     };
 
 
-    driver_context_t (const KerxSubTableFormat4 *table,
+    driver_context_t (const KerxSubTableFormat4 *table_,
 		      hb_aat_apply_context_t *c_) :
 		      hb_aat_apply_context_t *c_) :
 	c (c_),
 	c (c_),
+	table (table_),
 	action_type ((table->flags & ActionType) >> 30),
 	action_type ((table->flags & ActionType) >> 30),
 	ankrData ((HBUINT16 *) ((const char *) &table->machine + (table->flags & Offset))),
 	ankrData ((HBUINT16 *) ((const char *) &table->machine + (table->flags & Offset))),
 	mark_set (false),
 	mark_set (false),
 	mark (0) {}
 	mark (0) {}
 
 
-    bool is_actionable (hb_buffer_t *buffer HB_UNUSED,
-			StateTableDriver<Types, EntryData> *driver HB_UNUSED,
-			const Entry<EntryData> &entry)
-    { return entry.data.ankrActionIndex != 0xFFFF; }
     void transition (hb_buffer_t *buffer,
     void transition (hb_buffer_t *buffer,
-		     StateTableDriver<Types, EntryData> *driver,
+		     StateTableDriver<Types, EntryData, Flags> *driver,
 		     const Entry<EntryData> &entry)
 		     const Entry<EntryData> &entry)
     {
     {
       if (mark_set && entry.data.ankrActionIndex != 0xFFFF && buffer->idx < buffer->len)
       if (mark_set && entry.data.ankrActionIndex != 0xFFFF && buffer->idx < buffer->len)
@@ -634,8 +637,10 @@ struct KerxSubTableFormat4
       }
       }
     }
     }
 
 
-    private:
+    public:
     hb_aat_apply_context_t *c;
     hb_aat_apply_context_t *c;
+    const KerxSubTableFormat4 *table;
+    private:
     unsigned int action_type;
     unsigned int action_type;
     const HBUINT16 *ankrData;
     const HBUINT16 *ankrData;
     bool mark_set;
     bool mark_set;
@@ -648,12 +653,7 @@ struct KerxSubTableFormat4
 
 
     driver_context_t dc (this, c);
     driver_context_t dc (this, c);
 
 
-    StateTableDriver<Types, EntryData> driver (machine, c->font->face);
-
-    if (driver.is_idempotent_on_all_out_of_bounds (&dc, c) &&
-	!(c->buffer_digest.may_have (c->left_set) &&
-	  c->buffer_digest.may_have (c->right_set)))
-      return_trace (false);
+    StateTableDriver<Types, EntryData, Flags> driver (machine, c->font->face);
 
 
     driver.drive (&dc, c);
     driver.drive (&dc, c);
 
 
@@ -735,10 +735,6 @@ struct KerxSubTableFormat6
     if (header.coverage & header.Backwards)
     if (header.coverage & header.Backwards)
       return_trace (false);
       return_trace (false);
 
 
-    if (!(c->buffer_digest.may_have (c->left_set) &&
-	  c->buffer_digest.may_have (c->right_set)))
-      return_trace (false);
-
     accelerator_t accel (*this, c);
     accelerator_t accel (*this, c);
     hb_kern_machine_t<accelerator_t> machine (accel, header.coverage & header.CrossStream);
     hb_kern_machine_t<accelerator_t> machine (accel, header.coverage & header.CrossStream);
     machine.kern (c->font, c->buffer, c->plan->kern_mask);
     machine.kern (c->font, c->buffer, c->plan->kern_mask);
@@ -793,7 +789,7 @@ struct KerxSubTableFormat6
 
 
     int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
     int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
     {
     {
-      if (!c->left_set[left] || !c->right_set[right]) return 0;
+      if (!(*c->left_set)[left] || !(*c->right_set)[right]) return 0;
       return table.get_kerning (left, right, c);
       return table.get_kerning (left, right, c);
     }
     }
   };
   };
@@ -925,7 +921,7 @@ struct KerxSubTable
  * The 'kerx' Table
  * The 'kerx' Table
  */
  */
 
 
-using kern_accelerator_data_t = hb_vector_t<hb_pair_t<hb_set_digest_t, hb_set_digest_t>>;
+using kern_accelerator_data_t = hb_vector_t<hb_pair_t<hb_bit_set_t, hb_bit_set_t>>;
 
 
 template <typename T>
 template <typename T>
 struct KerxTable
 struct KerxTable
@@ -985,15 +981,10 @@ struct KerxTable
   }
   }
 
 
   bool apply (AAT::hb_aat_apply_context_t *c,
   bool apply (AAT::hb_aat_apply_context_t *c,
-	      const kern_accelerator_data_t *accel_data = nullptr) const
+	      const kern_accelerator_data_t &accel_data) const
   {
   {
     c->buffer->unsafe_to_concat ();
     c->buffer->unsafe_to_concat ();
 
 
-    if (c->buffer->len < HB_AAT_BUFFER_DIGEST_THRESHOLD)
-      c->buffer_digest = c->buffer->digest ();
-    else
-      c->buffer_digest = hb_set_digest_t::full ();
-
     typedef typename T::SubTable SubTable;
     typedef typename T::SubTable SubTable;
 
 
     bool ret = false;
     bool ret = false;
@@ -1037,15 +1028,8 @@ struct KerxTable
       if (reverse)
       if (reverse)
 	c->buffer->reverse ();
 	c->buffer->reverse ();
 
 
-      if (accel_data)
-      {
-	c->left_set = (*accel_data)[i].first;
-	c->right_set = (*accel_data)[i].second;
-      }
-      else
-      {
-        c->left_set = c->right_set = hb_set_digest_t::full ();
-      }
+      c->left_set = &accel_data[i].first;
+      c->right_set = &accel_data[i].second;
 
 
       {
       {
 	/* See comment in sanitize() for conditional here. */
 	/* See comment in sanitize() for conditional here. */
@@ -1122,7 +1106,7 @@ struct KerxTable
     unsigned int count = thiz()->tableCount;
     unsigned int count = thiz()->tableCount;
     for (unsigned int i = 0; i < count; i++)
     for (unsigned int i = 0; i < count; i++)
     {
     {
-      hb_set_digest_t left_set, right_set;
+      hb_bit_set_t left_set, right_set;
       st->collect_glyphs (left_set, right_set, num_glyphs);
       st->collect_glyphs (left_set, right_set, num_glyphs);
       accel_data.push (hb_pair (left_set, right_set));
       accel_data.push (hb_pair (left_set, right_set));
       st = &StructAfter<SubTable> (*st);
       st = &StructAfter<SubTable> (*st);
@@ -1148,7 +1132,7 @@ struct KerxTable
 
 
     bool apply (AAT::hb_aat_apply_context_t *c) const
     bool apply (AAT::hb_aat_apply_context_t *c) const
     {
     {
-      return table->apply (c, &accel_data);
+      return table->apply (c, accel_data);
     }
     }
 
 
     hb_blob_ptr_t<T> table;
     hb_blob_ptr_t<T> table;

+ 240 - 155
thirdparty/harfbuzz/src/hb-aat-layout-morx-table.hh

@@ -29,6 +29,7 @@
 
 
 #include "hb-open-type.hh"
 #include "hb-open-type.hh"
 #include "hb-aat-layout-common.hh"
 #include "hb-aat-layout-common.hh"
+#include "hb-ot-layout.hh"
 #include "hb-ot-layout-common.hh"
 #include "hb-ot-layout-common.hh"
 #include "hb-ot-layout-gdef-table.hh"
 #include "hb-ot-layout-gdef-table.hh"
 #include "hb-aat-map.hh"
 #include "hb-aat-map.hh"
@@ -53,35 +54,40 @@ struct RearrangementSubtable
 
 
   typedef void EntryData;
   typedef void EntryData;
 
 
-  struct driver_context_t
+  enum Flags
   {
   {
-    static constexpr bool in_place = true;
-    enum Flags
-    {
-      MarkFirst		= 0x8000,	/* If set, make the current glyph the first
+    MarkFirst		= 0x8000,	/* If set, make the current glyph the first
 					 * glyph to be rearranged. */
 					 * glyph to be rearranged. */
-      DontAdvance	= 0x4000,	/* If set, don't advance to the next glyph
+    DontAdvance		= 0x4000,	/* If set, don't advance to the next glyph
 					 * before going to the new state. This means
 					 * before going to the new state. This means
 					 * that the glyph index doesn't change, even
 					 * that the glyph index doesn't change, even
 					 * if the glyph at that index has changed. */
 					 * if the glyph at that index has changed. */
-      MarkLast		= 0x2000,	/* If set, make the current glyph the last
+    MarkLast		= 0x2000,	/* If set, make the current glyph the last
 					 * glyph to be rearranged. */
 					 * glyph to be rearranged. */
-      Reserved		= 0x1FF0,	/* These bits are reserved and should be set to 0. */
-      Verb		= 0x000F,	/* The type of rearrangement specified. */
-    };
+    Reserved		= 0x1FF0,	/* These bits are reserved and should be set to 0. */
+    Verb		= 0x000F,	/* The type of rearrangement specified. */
+  };
+
+  bool is_action_initiable (const Entry<EntryData> &entry) const
+  {
+    return (entry.flags & MarkFirst);
+  }
+  bool is_actionable (const Entry<EntryData> &entry) const
+  {
+    return (entry.flags & Verb);
+  }
+
+  struct driver_context_t
+  {
+    static constexpr bool in_place = true;
 
 
-    driver_context_t (const RearrangementSubtable *table HB_UNUSED) :
+    driver_context_t (const RearrangementSubtable *table_) :
 	ret (false),
 	ret (false),
+	table (table_),
 	start (0), end (0) {}
 	start (0), end (0) {}
 
 
-    bool is_actionable (hb_buffer_t *buffer HB_UNUSED,
-			StateTableDriver<Types, EntryData> *driver HB_UNUSED,
-			const Entry<EntryData> &entry) const
-    {
-      return (entry.flags & Verb) && start < end;
-    }
     void transition (hb_buffer_t *buffer,
     void transition (hb_buffer_t *buffer,
-		     StateTableDriver<Types, EntryData> *driver,
+		     StateTableDriver<Types, EntryData, Flags> *driver,
 		     const Entry<EntryData> &entry)
 		     const Entry<EntryData> &entry)
     {
     {
       unsigned int flags = entry.flags;
       unsigned int flags = entry.flags;
@@ -158,6 +164,7 @@ struct RearrangementSubtable
 
 
     public:
     public:
     bool ret;
     bool ret;
+    const RearrangementSubtable *table;
     private:
     private:
     unsigned int start;
     unsigned int start;
     unsigned int end;
     unsigned int end;
@@ -169,11 +176,13 @@ struct RearrangementSubtable
 
 
     driver_context_t dc (this);
     driver_context_t dc (this);
 
 
-    StateTableDriver<Types, EntryData> driver (machine, c->face);
+    StateTableDriver<Types, EntryData, Flags> driver (machine, c->face);
 
 
-    if (driver.is_idempotent_on_all_out_of_bounds (&dc, c) &&
-	!c->buffer_digest.may_have (c->machine_glyph_set))
+    if (!c->buffer_intersects_machine ())
+    {
+      (void) c->buffer->message (c->font, "skipped chainsubtable because no glyph matches");
       return_trace (false);
       return_trace (false);
+    }
 
 
     driver.drive (&dc, c);
     driver.drive (&dc, c);
 
 
@@ -207,39 +216,40 @@ struct ContextualSubtable
     DEFINE_SIZE_STATIC (4);
     DEFINE_SIZE_STATIC (4);
   };
   };
 
 
+  enum Flags
+  {
+    SetMark		= 0x8000,	/* If set, make the current glyph the marked glyph. */
+    DontAdvance		= 0x4000,	/* If set, don't advance to the next glyph before
+					 * going to the new state. */
+    Reserved		= 0x3FFF,	/* These bits are reserved and should be set to 0. */
+  };
+
+  bool is_action_initiable (const Entry<EntryData> &entry) const
+  {
+    return (entry.flags & SetMark);
+  }
+  bool is_actionable (const Entry<EntryData> &entry) const
+  {
+    return entry.data.markIndex != 0xFFFF || entry.data.currentIndex != 0xFFFF;
+  }
+
   struct driver_context_t
   struct driver_context_t
   {
   {
     static constexpr bool in_place = true;
     static constexpr bool in_place = true;
-    enum Flags
-    {
-      SetMark		= 0x8000,	/* If set, make the current glyph the marked glyph. */
-      DontAdvance	= 0x4000,	/* If set, don't advance to the next glyph before
-					 * going to the new state. */
-      Reserved		= 0x3FFF,	/* These bits are reserved and should be set to 0. */
-    };
 
 
     driver_context_t (const ContextualSubtable *table_,
     driver_context_t (const ContextualSubtable *table_,
 			     hb_aat_apply_context_t *c_) :
 			     hb_aat_apply_context_t *c_) :
 	ret (false),
 	ret (false),
 	c (c_),
 	c (c_),
+	table (table_),
 	gdef (*c->gdef_table),
 	gdef (*c->gdef_table),
 	mark_set (false),
 	mark_set (false),
 	has_glyph_classes (gdef.has_glyph_classes ()),
 	has_glyph_classes (gdef.has_glyph_classes ()),
 	mark (0),
 	mark (0),
-	table (table_),
 	subs (table+table->substitutionTables) {}
 	subs (table+table->substitutionTables) {}
 
 
-    bool is_actionable (hb_buffer_t *buffer,
-			StateTableDriver<Types, EntryData> *driver,
-			const Entry<EntryData> &entry) const
-    {
-      if (buffer->idx == buffer->len && !mark_set)
-	return false;
-
-      return entry.data.markIndex != 0xFFFF || entry.data.currentIndex != 0xFFFF;
-    }
     void transition (hb_buffer_t *buffer,
     void transition (hb_buffer_t *buffer,
-		     StateTableDriver<Types, EntryData> *driver,
+		     StateTableDriver<Types, EntryData, Flags> *driver,
 		     const Entry<EntryData> &entry)
 		     const Entry<EntryData> &entry)
     {
     {
       /* Looks like CoreText applies neither mark nor current substitution for
       /* Looks like CoreText applies neither mark nor current substitution for
@@ -271,8 +281,9 @@ struct ContextualSubtable
       if (replacement)
       if (replacement)
       {
       {
 	buffer->unsafe_to_break (mark, hb_min (buffer->idx + 1, buffer->len));
 	buffer->unsafe_to_break (mark, hb_min (buffer->idx + 1, buffer->len));
-	buffer->info[mark].codepoint = *replacement;
-	c->buffer_digest.add (*replacement);
+	hb_codepoint_t glyph = *replacement;
+	buffer->info[mark].codepoint = glyph;
+	c->buffer_glyph_set.add (glyph);
 	if (has_glyph_classes)
 	if (has_glyph_classes)
 	  _hb_glyph_info_set_glyph_props (&buffer->info[mark],
 	  _hb_glyph_info_set_glyph_props (&buffer->info[mark],
 					  gdef.get_glyph_props (*replacement));
 					  gdef.get_glyph_props (*replacement));
@@ -301,8 +312,9 @@ struct ContextualSubtable
       }
       }
       if (replacement)
       if (replacement)
       {
       {
-	buffer->info[idx].codepoint = *replacement;
-	c->buffer_digest.add (*replacement);
+	hb_codepoint_t glyph = *replacement;
+	buffer->info[idx].codepoint = glyph;
+	c->buffer_glyph_set.add (glyph);
 	if (has_glyph_classes)
 	if (has_glyph_classes)
 	  _hb_glyph_info_set_glyph_props (&buffer->info[idx],
 	  _hb_glyph_info_set_glyph_props (&buffer->info[idx],
 					  gdef.get_glyph_props (*replacement));
 					  gdef.get_glyph_props (*replacement));
@@ -318,13 +330,13 @@ struct ContextualSubtable
 
 
     public:
     public:
     bool ret;
     bool ret;
-    private:
     hb_aat_apply_context_t *c;
     hb_aat_apply_context_t *c;
+    const ContextualSubtable *table;
+    private:
     const OT::GDEF &gdef;
     const OT::GDEF &gdef;
     bool mark_set;
     bool mark_set;
     bool has_glyph_classes;
     bool has_glyph_classes;
     unsigned int mark;
     unsigned int mark;
-    const ContextualSubtable *table;
     const UnsizedListOfOffset16To<Lookup<HBGlyphID16>, HBUINT, void, false> &subs;
     const UnsizedListOfOffset16To<Lookup<HBGlyphID16>, HBUINT, void, false> &subs;
   };
   };
 
 
@@ -334,11 +346,13 @@ struct ContextualSubtable
 
 
     driver_context_t dc (this, c);
     driver_context_t dc (this, c);
 
 
-    StateTableDriver<Types, EntryData> driver (machine, c->face);
+    StateTableDriver<Types, EntryData, Flags> driver (machine, c->face);
 
 
-    if (driver.is_idempotent_on_all_out_of_bounds (&dc, c) &&
-	!c->buffer_digest.may_have (c->machine_glyph_set))
+    if (!c->buffer_intersects_machine ())
+    {
+      (void) c->buffer->message (c->font, "skipped chainsubtable because no glyph matches");
       return_trace (false);
       return_trace (false);
+    }
 
 
     driver.drive (&dc, c);
     driver.drive (&dc, c);
 
 
@@ -389,6 +403,16 @@ struct LigatureEntry;
 template <>
 template <>
 struct LigatureEntry<true>
 struct LigatureEntry<true>
 {
 {
+
+  struct EntryData
+  {
+    HBUINT16	ligActionIndex;	/* Index to the first ligActionTable entry
+				 * for processing this group, if indicated
+				 * by the flags. */
+    public:
+    DEFINE_SIZE_STATIC (2);
+  };
+
   enum Flags
   enum Flags
   {
   {
     SetComponent	= 0x8000,	/* Push this glyph onto the component stack for
     SetComponent	= 0x8000,	/* Push this glyph onto the component stack for
@@ -400,14 +424,8 @@ struct LigatureEntry<true>
     Reserved		= 0x1FFF,	/* These bits are reserved and should be set to 0. */
     Reserved		= 0x1FFF,	/* These bits are reserved and should be set to 0. */
   };
   };
 
 
-  struct EntryData
-  {
-    HBUINT16	ligActionIndex;	/* Index to the first ligActionTable entry
-				 * for processing this group, if indicated
-				 * by the flags. */
-    public:
-    DEFINE_SIZE_STATIC (2);
-  };
+  static bool initiateAction (const Entry<EntryData> &entry)
+  { return entry.flags & SetComponent; }
 
 
   static bool performAction (const Entry<EntryData> &entry)
   static bool performAction (const Entry<EntryData> &entry)
   { return entry.flags & PerformAction; }
   { return entry.flags & PerformAction; }
@@ -418,6 +436,8 @@ struct LigatureEntry<true>
 template <>
 template <>
 struct LigatureEntry<false>
 struct LigatureEntry<false>
 {
 {
+  typedef void EntryData;
+
   enum Flags
   enum Flags
   {
   {
     SetComponent	= 0x8000,	/* Push this glyph onto the component stack for
     SetComponent	= 0x8000,	/* Push this glyph onto the component stack for
@@ -429,7 +449,8 @@ struct LigatureEntry<false>
 					 * multiple of 4. */
 					 * multiple of 4. */
   };
   };
 
 
-  typedef void EntryData;
+  static bool initiateAction (const Entry<EntryData> &entry)
+  { return entry.flags & SetComponent; }
 
 
   static bool performAction (const Entry<EntryData> &entry)
   static bool performAction (const Entry<EntryData> &entry)
   { return entry.flags & Offset; }
   { return entry.flags & Offset; }
@@ -447,13 +468,23 @@ struct LigatureSubtable
   typedef LigatureEntry<Types::extended> LigatureEntryT;
   typedef LigatureEntry<Types::extended> LigatureEntryT;
   typedef typename LigatureEntryT::EntryData EntryData;
   typedef typename LigatureEntryT::EntryData EntryData;
 
 
+  enum Flags
+  {
+    DontAdvance	= LigatureEntryT::DontAdvance,
+  };
+
+  bool is_action_initiable (const Entry<EntryData> &entry) const
+  {
+    return LigatureEntryT::initiateAction (entry);
+  }
+  bool is_actionable (const Entry<EntryData> &entry) const
+  {
+    return LigatureEntryT::performAction (entry);
+  }
+
   struct driver_context_t
   struct driver_context_t
   {
   {
     static constexpr bool in_place = false;
     static constexpr bool in_place = false;
-    enum
-    {
-      DontAdvance	= LigatureEntryT::DontAdvance,
-    };
     enum LigActionFlags
     enum LigActionFlags
     {
     {
       LigActionLast	= 0x80000000,	/* This is the last action in the list. This also
       LigActionLast	= 0x80000000,	/* This is the last action in the list. This also
@@ -476,14 +507,8 @@ struct LigatureSubtable
 	ligature (table+table->ligature),
 	ligature (table+table->ligature),
 	match_length (0) {}
 	match_length (0) {}
 
 
-    bool is_actionable (hb_buffer_t *buffer HB_UNUSED,
-			StateTableDriver<Types, EntryData> *driver HB_UNUSED,
-			const Entry<EntryData> &entry) const
-    {
-      return LigatureEntryT::performAction (entry);
-    }
     void transition (hb_buffer_t *buffer,
     void transition (hb_buffer_t *buffer,
-		     StateTableDriver<Types, EntryData> *driver,
+		     StateTableDriver<Types, EntryData, Flags> *driver,
 		     const Entry<EntryData> &entry)
 		     const Entry<EntryData> &entry)
     {
     {
       DEBUG_MSG (APPLY, nullptr, "Ligature transition at %u", buffer->idx);
       DEBUG_MSG (APPLY, nullptr, "Ligature transition at %u", buffer->idx);
@@ -564,7 +589,7 @@ struct LigatureSubtable
 	    {
 	    {
 	      DEBUG_MSG (APPLY, nullptr, "Skipping ligature component");
 	      DEBUG_MSG (APPLY, nullptr, "Skipping ligature component");
 	      if (unlikely (!buffer->move_to (match_positions[--match_length % ARRAY_LENGTH (match_positions)]))) return;
 	      if (unlikely (!buffer->move_to (match_positions[--match_length % ARRAY_LENGTH (match_positions)]))) return;
-	      buffer->cur().unicode_props() |= UPROPS_MASK_IGNORABLE;
+	      _hb_glyph_info_set_default_ignorable (&buffer->cur());
 	      if (unlikely (!buffer->replace_glyph (DELETED_GLYPH))) return;
 	      if (unlikely (!buffer->replace_glyph (DELETED_GLYPH))) return;
 	    }
 	    }
 
 
@@ -581,9 +606,9 @@ struct LigatureSubtable
 
 
     public:
     public:
     bool ret;
     bool ret;
-    private:
     hb_aat_apply_context_t *c;
     hb_aat_apply_context_t *c;
     const LigatureSubtable *table;
     const LigatureSubtable *table;
+    private:
     const UnsizedArrayOf<HBUINT32> &ligAction;
     const UnsizedArrayOf<HBUINT32> &ligAction;
     const UnsizedArrayOf<HBUINT16> &component;
     const UnsizedArrayOf<HBUINT16> &component;
     const UnsizedArrayOf<HBGlyphID16> &ligature;
     const UnsizedArrayOf<HBGlyphID16> &ligature;
@@ -597,11 +622,13 @@ struct LigatureSubtable
 
 
     driver_context_t dc (this, c);
     driver_context_t dc (this, c);
 
 
-    StateTableDriver<Types, EntryData> driver (machine, c->face);
+    StateTableDriver<Types, EntryData, Flags> driver (machine, c->face);
 
 
-    if (driver.is_idempotent_on_all_out_of_bounds (&dc, c) &&
-	!c->buffer_digest.may_have (c->machine_glyph_set))
+    if (!c->buffer_intersects_machine ())
+    {
+      (void) c->buffer->message (c->font, "skipped chainsubtable because no glyph matches");
       return_trace (false);
       return_trace (false);
+    }
 
 
     driver.drive (&dc, c);
     driver.drive (&dc, c);
 
 
@@ -638,6 +665,12 @@ struct NoncontextualSubtable
   {
   {
     TRACE_APPLY (this);
     TRACE_APPLY (this);
 
 
+    if (!c->buffer_intersects_machine ())
+    {
+      (void) c->buffer->message (c->font, "skipped chainsubtable because no glyph matches");
+      return_trace (false);
+    }
+
     const OT::GDEF &gdef (*c->gdef_table);
     const OT::GDEF &gdef (*c->gdef_table);
     bool has_glyph_classes = gdef.has_glyph_classes ();
     bool has_glyph_classes = gdef.has_glyph_classes ();
 
 
@@ -670,8 +703,9 @@ struct NoncontextualSubtable
       const HBGlyphID16 *replacement = substitute.get_value (info[i].codepoint, num_glyphs);
       const HBGlyphID16 *replacement = substitute.get_value (info[i].codepoint, num_glyphs);
       if (replacement)
       if (replacement)
       {
       {
-	info[i].codepoint = *replacement;
-	c->buffer_digest.add (*replacement);
+	hb_codepoint_t glyph = *replacement;
+	info[i].codepoint = glyph;
+	c->buffer_glyph_set.add (glyph);
 	if (has_glyph_classes)
 	if (has_glyph_classes)
 	  _hb_glyph_info_set_glyph_props (&info[i],
 	  _hb_glyph_info_set_glyph_props (&info[i],
 					  gdef.get_glyph_props (*replacement));
 					  gdef.get_glyph_props (*replacement));
@@ -682,6 +716,12 @@ struct NoncontextualSubtable
     return_trace (ret);
     return_trace (ret);
   }
   }
 
 
+  template <typename set_t>
+  void collect_initial_glyphs (set_t &glyphs, unsigned num_glyphs) const
+  {
+    substitute.collect_glyphs (glyphs, num_glyphs);
+  }
+
   bool sanitize (hb_sanitize_context_t *c) const
   bool sanitize (hb_sanitize_context_t *c) const
   {
   {
     TRACE_SANITIZE (this);
     TRACE_SANITIZE (this);
@@ -715,73 +755,78 @@ struct InsertionSubtable
     DEFINE_SIZE_STATIC (4);
     DEFINE_SIZE_STATIC (4);
   };
   };
 
 
+  enum Flags
+  {
+    SetMark		= 0x8000,     /* If set, mark the current glyph. */
+    DontAdvance		= 0x4000,     /* If set, don't advance to the next glyph before
+				       * going to the new state.  This does not mean
+				       * that the glyph pointed to is the same one as
+				       * before. If you've made insertions immediately
+				       * downstream of the current glyph, the next glyph
+				       * processed would in fact be the first one
+				       * inserted. */
+    CurrentIsKashidaLike= 0x2000,     /* If set, and the currentInsertList is nonzero,
+				       * then the specified glyph list will be inserted
+				       * as a kashida-like insertion, either before or
+				       * after the current glyph (depending on the state
+				       * of the currentInsertBefore flag). If clear, and
+				       * the currentInsertList is nonzero, then the
+				       * specified glyph list will be inserted as a
+				       * split-vowel-like insertion, either before or
+				       * after the current glyph (depending on the state
+				       * of the currentInsertBefore flag). */
+    MarkedIsKashidaLike= 0x1000,      /* If set, and the markedInsertList is nonzero,
+				       * then the specified glyph list will be inserted
+				       * as a kashida-like insertion, either before or
+				       * after the marked glyph (depending on the state
+				       * of the markedInsertBefore flag). If clear, and
+				       * the markedInsertList is nonzero, then the
+				       * specified glyph list will be inserted as a
+				       * split-vowel-like insertion, either before or
+				       * after the marked glyph (depending on the state
+				       * of the markedInsertBefore flag). */
+    CurrentInsertBefore= 0x0800,      /* If set, specifies that insertions are to be made
+				       * to the left of the current glyph. If clear,
+				       * they're made to the right of the current glyph. */
+    MarkedInsertBefore= 0x0400,	      /* If set, specifies that insertions are to be
+				       * made to the left of the marked glyph. If clear,
+				       * they're made to the right of the marked glyph. */
+    CurrentInsertCount= 0x3E0,	      /* This 5-bit field is treated as a count of the
+				       * number of glyphs to insert at the current
+				       * position. Since zero means no insertions, the
+				       * largest number of insertions at any given
+				       * current location is 31 glyphs. */
+    MarkedInsertCount= 0x001F,	      /* This 5-bit field is treated as a count of the
+				       * number of glyphs to insert at the marked
+				       * position. Since zero means no insertions, the
+				       * largest number of insertions at any given
+				       * marked location is 31 glyphs. */
+  };
+
+  bool is_action_initiable (const Entry<EntryData> &entry) const
+  {
+    return (entry.flags & SetMark);
+  }
+  bool is_actionable (const Entry<EntryData> &entry) const
+  {
+    return (entry.flags & (CurrentInsertCount | MarkedInsertCount)) &&
+	   (entry.data.currentInsertIndex != 0xFFFF ||entry.data.markedInsertIndex != 0xFFFF);
+  }
+
   struct driver_context_t
   struct driver_context_t
   {
   {
     static constexpr bool in_place = false;
     static constexpr bool in_place = false;
-    enum Flags
-    {
-      SetMark		= 0x8000,	/* If set, mark the current glyph. */
-      DontAdvance	= 0x4000,	/* If set, don't advance to the next glyph before
-					 * going to the new state.  This does not mean
-					 * that the glyph pointed to is the same one as
-					 * before. If you've made insertions immediately
-					 * downstream of the current glyph, the next glyph
-					 * processed would in fact be the first one
-					 * inserted. */
-      CurrentIsKashidaLike= 0x2000,	/* If set, and the currentInsertList is nonzero,
-					 * then the specified glyph list will be inserted
-					 * as a kashida-like insertion, either before or
-					 * after the current glyph (depending on the state
-					 * of the currentInsertBefore flag). If clear, and
-					 * the currentInsertList is nonzero, then the
-					 * specified glyph list will be inserted as a
-					 * split-vowel-like insertion, either before or
-					 * after the current glyph (depending on the state
-					 * of the currentInsertBefore flag). */
-      MarkedIsKashidaLike= 0x1000,	/* If set, and the markedInsertList is nonzero,
-					 * then the specified glyph list will be inserted
-					 * as a kashida-like insertion, either before or
-					 * after the marked glyph (depending on the state
-					 * of the markedInsertBefore flag). If clear, and
-					 * the markedInsertList is nonzero, then the
-					 * specified glyph list will be inserted as a
-					 * split-vowel-like insertion, either before or
-					 * after the marked glyph (depending on the state
-					 * of the markedInsertBefore flag). */
-      CurrentInsertBefore= 0x0800,	/* If set, specifies that insertions are to be made
-					 * to the left of the current glyph. If clear,
-					 * they're made to the right of the current glyph. */
-      MarkedInsertBefore= 0x0400,	/* If set, specifies that insertions are to be
-					 * made to the left of the marked glyph. If clear,
-					 * they're made to the right of the marked glyph. */
-      CurrentInsertCount= 0x3E0,	/* This 5-bit field is treated as a count of the
-					 * number of glyphs to insert at the current
-					 * position. Since zero means no insertions, the
-					 * largest number of insertions at any given
-					 * current location is 31 glyphs. */
-      MarkedInsertCount= 0x001F,	/* This 5-bit field is treated as a count of the
-					 * number of glyphs to insert at the marked
-					 * position. Since zero means no insertions, the
-					 * largest number of insertions at any given
-					 * marked location is 31 glyphs. */
-    };
 
 
-    driver_context_t (const InsertionSubtable *table,
+    driver_context_t (const InsertionSubtable *table_,
 		      hb_aat_apply_context_t *c_) :
 		      hb_aat_apply_context_t *c_) :
 	ret (false),
 	ret (false),
 	c (c_),
 	c (c_),
+	table (table_),
 	mark (0),
 	mark (0),
 	insertionAction (table+table->insertionAction) {}
 	insertionAction (table+table->insertionAction) {}
 
 
-    bool is_actionable (hb_buffer_t *buffer HB_UNUSED,
-			StateTableDriver<Types, EntryData> *driver HB_UNUSED,
-			const Entry<EntryData> &entry) const
-    {
-      return (entry.flags & (CurrentInsertCount | MarkedInsertCount)) &&
-	     (entry.data.currentInsertIndex != 0xFFFF ||entry.data.markedInsertIndex != 0xFFFF);
-    }
     void transition (hb_buffer_t *buffer,
     void transition (hb_buffer_t *buffer,
-		     StateTableDriver<Types, EntryData> *driver,
+		     StateTableDriver<Types, EntryData, Flags> *driver,
 		     const Entry<EntryData> &entry)
 		     const Entry<EntryData> &entry)
     {
     {
       unsigned int flags = entry.flags;
       unsigned int flags = entry.flags;
@@ -807,7 +852,7 @@ struct InsertionSubtable
 	/* TODO We ignore KashidaLike setting. */
 	/* TODO We ignore KashidaLike setting. */
 	if (unlikely (!buffer->replace_glyphs (0, count, glyphs))) return;
 	if (unlikely (!buffer->replace_glyphs (0, count, glyphs))) return;
 	for (unsigned int i = 0; i < count; i++)
 	for (unsigned int i = 0; i < count; i++)
-	  c->buffer_digest.add (glyphs[i]);
+	  c->buffer_glyph_set.add (glyphs[i]);
 	ret = true;
 	ret = true;
 	if (buffer->idx < buffer->len && !before)
 	if (buffer->idx < buffer->len && !before)
 	  buffer->skip_glyph ();
 	  buffer->skip_glyph ();
@@ -861,8 +906,9 @@ struct InsertionSubtable
 
 
     public:
     public:
     bool ret;
     bool ret;
-    private:
     hb_aat_apply_context_t *c;
     hb_aat_apply_context_t *c;
+    const InsertionSubtable *table;
+    private:
     unsigned int mark;
     unsigned int mark;
     const UnsizedArrayOf<HBGlyphID16> &insertionAction;
     const UnsizedArrayOf<HBGlyphID16> &insertionAction;
   };
   };
@@ -873,11 +919,13 @@ struct InsertionSubtable
 
 
     driver_context_t dc (this, c);
     driver_context_t dc (this, c);
 
 
-    StateTableDriver<Types, EntryData> driver (machine, c->face);
+    StateTableDriver<Types, EntryData, Flags> driver (machine, c->face);
 
 
-    if (driver.is_idempotent_on_all_out_of_bounds (&dc, c) &&
-	!c->buffer_digest.may_have (c->machine_glyph_set))
+    if (!c->buffer_intersects_machine ())
+    {
+      (void) c->buffer->message (c->font, "skipped chainsubtable because no glyph matches");
       return_trace (false);
       return_trace (false);
+    }
 
 
     driver.drive (&dc, c);
     driver.drive (&dc, c);
 
 
@@ -935,24 +983,33 @@ struct hb_accelerate_subtables_context_t :
     friend struct hb_aat_layout_lookup_accelerator_t;
     friend struct hb_aat_layout_lookup_accelerator_t;
 
 
     public:
     public:
-    hb_set_digest_t digest;
+    hb_bit_set_t glyph_set;
+    mutable hb_aat_class_cache_t class_cache;
 
 
     template <typename T>
     template <typename T>
     auto init_ (const T &obj_, unsigned num_glyphs, hb_priority<1>) HB_AUTO_RETURN
     auto init_ (const T &obj_, unsigned num_glyphs, hb_priority<1>) HB_AUTO_RETURN
     (
     (
-      obj_.machine.collect_glyphs (this->digest, num_glyphs)
+      obj_.machine.collect_initial_glyphs (glyph_set, num_glyphs, obj_)
     )
     )
 
 
     template <typename T>
     template <typename T>
     void init_ (const T &obj_, unsigned num_glyphs, hb_priority<0>)
     void init_ (const T &obj_, unsigned num_glyphs, hb_priority<0>)
     {
     {
-      digest = digest.full ();
+      obj_.collect_initial_glyphs (glyph_set, num_glyphs);
     }
     }
 
 
     template <typename T>
     template <typename T>
     void init (const T &obj_, unsigned num_glyphs)
     void init (const T &obj_, unsigned num_glyphs)
     {
     {
+      glyph_set.init ();
       init_ (obj_, num_glyphs, hb_prioritize);
       init_ (obj_, num_glyphs, hb_prioritize);
+      class_cache.clear ();
+    }
+
+    void
+    fini ()
+    {
+      glyph_set.fini ();
     }
     }
   };
   };
 
 
@@ -999,12 +1056,21 @@ struct hb_aat_layout_chain_accelerator_t
     if (unlikely (!thiz))
     if (unlikely (!thiz))
       return nullptr;
       return nullptr;
 
 
+    thiz->count = count;
+
     hb_accelerate_subtables_context_t c_accelerate_subtables (thiz->subtables, num_glyphs);
     hb_accelerate_subtables_context_t c_accelerate_subtables (thiz->subtables, num_glyphs);
     chain.dispatch (&c_accelerate_subtables);
     chain.dispatch (&c_accelerate_subtables);
 
 
     return thiz;
     return thiz;
   }
   }
 
 
+  void destroy ()
+  {
+    for (unsigned i = 0; i < count; i++)
+      subtables[i].fini ();
+  }
+
+  unsigned count;
   hb_accelerate_subtables_context_t::hb_applicable_t subtables[HB_VAR_ARRAY];
   hb_accelerate_subtables_context_t::hb_applicable_t subtables[HB_VAR_ARRAY];
 };
 };
 
 
@@ -1152,15 +1218,19 @@ struct Chain
     {
     {
       bool reverse;
       bool reverse;
 
 
+      auto coverage = subtable->get_coverage ();
+
+      hb_mask_t subtable_flags = subtable->subFeatureFlags;
       if (hb_none (hb_iter (c->range_flags) |
       if (hb_none (hb_iter (c->range_flags) |
-		   hb_map ([&subtable] (const hb_aat_map_t::range_flags_t _) -> bool { return subtable->subFeatureFlags & (_.flags); })))
+		   hb_map ([subtable_flags] (const hb_aat_map_t::range_flags_t _) -> bool { return subtable_flags & (_.flags); })))
 	goto skip;
 	goto skip;
-      c->subtable_flags = subtable->subFeatureFlags;
-      c->machine_glyph_set = accel ? accel->subtables[i].digest : hb_set_digest_t::full ();
+      c->subtable_flags = subtable_flags;
+      c->machine_glyph_set = accel ? &accel->subtables[i].glyph_set : &Null(hb_bit_set_t);
+      c->machine_class_cache = accel ? &accel->subtables[i].class_cache : nullptr;
 
 
-      if (!(subtable->get_coverage() & ChainSubtable<Types>::AllDirections) &&
+      if (!(coverage & ChainSubtable<Types>::AllDirections) &&
 	  HB_DIRECTION_IS_VERTICAL (c->buffer->props.direction) !=
 	  HB_DIRECTION_IS_VERTICAL (c->buffer->props.direction) !=
-	  bool (subtable->get_coverage() & ChainSubtable<Types>::Vertical))
+	  bool (coverage & ChainSubtable<Types>::Vertical))
 	goto skip;
 	goto skip;
 
 
       /* Buffer contents is always in logical direction.  Determine if
       /* Buffer contents is always in logical direction.  Determine if
@@ -1190,9 +1260,9 @@ struct Chain
 				(the order opposite that of the characters, which
 				(the order opposite that of the characters, which
 				may be right-to-left or left-to-right).
 				may be right-to-left or left-to-right).
        */
        */
-      reverse = subtable->get_coverage () & ChainSubtable<Types>::Logical ?
-		bool (subtable->get_coverage () & ChainSubtable<Types>::Backwards) :
-		bool (subtable->get_coverage () & ChainSubtable<Types>::Backwards) !=
+      reverse = coverage & ChainSubtable<Types>::Logical ?
+		bool (coverage & ChainSubtable<Types>::Backwards) :
+		bool (coverage & ChainSubtable<Types>::Backwards) !=
 		HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction);
 		HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction);
 
 
       if (!c->buffer->message (c->font, "start chainsubtable %u", c->lookup_index))
       if (!c->buffer->message (c->font, "start chainsubtable %u", c->lookup_index))
@@ -1298,6 +1368,12 @@ struct mortmorx
       hb_sanitize_context_t sc;
       hb_sanitize_context_t sc;
       this->table = sc.reference_table<T> (face);
       this->table = sc.reference_table<T> (face);
 
 
+      if (unlikely (this->table->is_blocklisted (this->table.get_blob (), face)))
+      {
+        hb_blob_destroy (this->table.get_blob ());
+        this->table = hb_blob_get_empty ();
+      }
+
       this->chain_count = table->get_chain_count ();
       this->chain_count = table->get_chain_count ();
 
 
       this->accels = (hb_atomic_ptr_t<hb_aat_layout_chain_accelerator_t> *) hb_calloc (this->chain_count, sizeof (*accels));
       this->accels = (hb_atomic_ptr_t<hb_aat_layout_chain_accelerator_t> *) hb_calloc (this->chain_count, sizeof (*accels));
@@ -1311,7 +1387,11 @@ struct mortmorx
     ~accelerator_t ()
     ~accelerator_t ()
     {
     {
       for (unsigned int i = 0; i < this->chain_count; i++)
       for (unsigned int i = 0; i < this->chain_count; i++)
+      {
+	if (this->accels[i])
+	  this->accels[i]->destroy ();
 	hb_free (this->accels[i]);
 	hb_free (this->accels[i]);
+      }
       hb_free (this->accels);
       hb_free (this->accels);
       this->table.destroy ();
       this->table.destroy ();
     }
     }
@@ -1365,9 +1445,8 @@ struct mortmorx
 
 
   unsigned get_chain_count () const
   unsigned get_chain_count () const
   {
   {
-	  return chainCount;
+    return chainCount;
   }
   }
-
   void apply (hb_aat_apply_context_t *c,
   void apply (hb_aat_apply_context_t *c,
 	      const hb_aat_map_t &map,
 	      const hb_aat_map_t &map,
 	      const accelerator_t &accel) const
 	      const accelerator_t &accel) const
@@ -1376,10 +1455,7 @@ struct mortmorx
 
 
     c->buffer->unsafe_to_concat ();
     c->buffer->unsafe_to_concat ();
 
 
-    if (c->buffer->len < HB_AAT_BUFFER_DIGEST_THRESHOLD)
-      c->buffer_digest = c->buffer->digest ();
-    else
-      c->buffer_digest = hb_set_digest_t::full ();
+    c->setup_buffer_glyph_set ();
 
 
     c->set_lookup_index (0);
     c->set_lookup_index (0);
     const Chain<Types> *chain = &firstChain;
     const Chain<Types> *chain = &firstChain;
@@ -1428,8 +1504,17 @@ struct mortmorx
   DEFINE_SIZE_MIN (8);
   DEFINE_SIZE_MIN (8);
 };
 };
 
 
-struct morx : mortmorx<morx, ExtendedTypes, HB_AAT_TAG_morx> {};
-struct mort : mortmorx<mort, ObsoleteTypes, HB_AAT_TAG_mort> {};
+struct morx : mortmorx<morx, ExtendedTypes, HB_AAT_TAG_morx>
+{
+  HB_INTERNAL bool is_blocklisted (hb_blob_t *blob,
+                                   hb_face_t *face) const;
+};
+
+struct mort : mortmorx<mort, ObsoleteTypes, HB_AAT_TAG_mort>
+{
+  HB_INTERNAL bool is_blocklisted (hb_blob_t *blob,
+                                   hb_face_t *face) const;
+};
 
 
 struct morx_accelerator_t : morx::accelerator_t {
 struct morx_accelerator_t : morx::accelerator_t {
   morx_accelerator_t (hb_face_t *face) : morx::accelerator_t (face) {}
   morx_accelerator_t (hb_face_t *face) : morx::accelerator_t (face) {}

+ 93 - 91
thirdparty/harfbuzz/src/hb-aat-layout-trak-table.hh

@@ -48,17 +48,69 @@ struct TrackTableEntry
 
 
   float get_track_value () const { return track.to_float (); }
   float get_track_value () const { return track.to_float (); }
 
 
-  int get_value (const void *base, unsigned int index,
-		 unsigned int table_size) const
-  { return (base+valuesZ).as_array (table_size)[index]; }
+  float interpolate_at (unsigned int idx,
+			float ptem,
+			const void *base,
+			hb_array_t<const F16DOT16> size_table) const
+  {
+    const FWORD *values = (base+valuesZ).arrayZ;
+
+    float s0 = size_table[idx].to_float ();
+    float s1 = size_table[idx + 1].to_float ();
+    int v0 = values[idx];
+    int v1 = values[idx + 1];
+
+    // Deal with font bugs.
+    if (unlikely (s1 < s0))
+    { hb_swap (s0, s1); hb_swap (v0, v1); }
+    if (unlikely (ptem < s0)) return v0;
+    if (unlikely (ptem > s1)) return v1;
+    if (unlikely (s0 == s1)) return (v0 + v1) * 0.5f;
+
+    float t = (ptem - s0) / (s1 - s0);
+    return v0 + t * (v1 - v0);
+  }
+
+  float get_value (float ptem,
+		   const void *base,
+		   hb_array_t<const F16DOT16> size_table) const
+  {
+    const FWORD *values = (base+valuesZ).arrayZ;
+
+    unsigned int n_sizes = size_table.length;
+
+    /*
+     * Choose size.
+     */
+    if (!n_sizes) return 0.f;
+    if (n_sizes == 1) return values[0];
+
+    // At least two entries.
+
+    unsigned i;
+    for (i = 0; i < n_sizes; i++)
+      if (size_table[i].to_float () >= ptem)
+	break;
+
+    // Boundary conditions.
+    if (i == 0)       return values[0];
+    if (i == n_sizes) return values[n_sizes - 1];
+
+    // Exact match.
+    if (size_table[i].to_float () == ptem) return values[i];
+
+    // Interpolate.
+    return interpolate_at (i - 1, ptem, base, size_table);
+  }
 
 
   public:
   public:
-  bool sanitize (hb_sanitize_context_t *c, const void *base,
-		 unsigned int table_size) const
+  bool sanitize (hb_sanitize_context_t *c,
+		 const void *base,
+		 unsigned int n_sizes) const
   {
   {
     TRACE_SANITIZE (this);
     TRACE_SANITIZE (this);
     return_trace (likely (c->check_struct (this) &&
     return_trace (likely (c->check_struct (this) &&
-			  (valuesZ.sanitize (c, base, table_size))));
+			  (valuesZ.sanitize (c, base, n_sizes))));
   }
   }
 
 
   protected:
   protected:
@@ -76,58 +128,38 @@ struct TrackTableEntry
 
 
 struct TrackData
 struct TrackData
 {
 {
-  float interpolate_at (unsigned int idx,
-			float target_size,
-			const TrackTableEntry &trackTableEntry,
-			const void *base) const
+  float get_tracking (const void *base, float ptem, float track = 0.f) const
   {
   {
-    unsigned int sizes = nSizes;
-    hb_array_t<const F16DOT16> size_table ((base+sizeTable).arrayZ, sizes);
+    unsigned count = nTracks;
+    hb_array_t<const F16DOT16> size_table = (base+sizeTable).as_array (nSizes);
 
 
-    float s0 = size_table[idx].to_float ();
-    float s1 = size_table[idx + 1].to_float ();
-    float t = unlikely (s0 == s1) ? 0.f : (target_size - s0) / (s1 - s0);
-    return t * trackTableEntry.get_value (base, idx + 1, sizes) +
-	   (1.f - t) * trackTableEntry.get_value (base, idx, sizes);
-  }
+    if (!count) return 0.f;
+    if (count == 1) return trackTable[0].get_value (ptem, base, size_table);
 
 
-  int get_tracking (const void *base, float ptem) const
-  {
-    /*
-     * Choose track.
-     */
-    const TrackTableEntry *trackTableEntry = nullptr;
-    unsigned int count = nTracks;
-    for (unsigned int i = 0; i < count; i++)
-    {
-      /* Note: Seems like the track entries are sorted by values.  But the
-       * spec doesn't explicitly say that.  It just mentions it in the example. */
-
-      /* For now we only seek for track entries with zero tracking value */
-
-      if (trackTable[i].get_track_value () == 0.f)
-      {
-	trackTableEntry = &trackTable[i];
-	break;
-      }
-    }
-    if (!trackTableEntry) return 0;
+    // At least two entries.
 
 
-    /*
-     * Choose size.
-     */
-    unsigned int sizes = nSizes;
-    if (!sizes) return 0;
-    if (sizes == 1) return trackTableEntry->get_value (base, 0, sizes);
-
-    hb_array_t<const F16DOT16> size_table ((base+sizeTable).arrayZ, sizes);
-    unsigned int size_index;
-    for (size_index = 0; size_index < sizes - 1; size_index++)
-      if (size_table[size_index].to_float () >= ptem)
-	break;
+    unsigned i = 0;
+    unsigned j = count - 1;
+
+    // Find the two entries that track is between.
+    while (i + 1 < count && trackTable[i + 1].get_track_value () < track)
+      i++;
+    while (j > 0 && trackTable[j - 1].get_track_value () > track)
+      j--;
+
+    // Exact match.
+    if (i == j) return trackTable[i].get_value (ptem, base, size_table);
+
+    // Interpolate.
 
 
-    return roundf (interpolate_at (size_index ? size_index - 1 : 0, ptem,
-				   *trackTableEntry, base));
+    float t0 = trackTable[i].get_track_value ();
+    float t1 = trackTable[j].get_track_value ();
+
+    float t = (track - t0) / (t1 - t0);
+
+    float a = trackTable[i].get_value (ptem, base, size_table);
+    float b = trackTable[j].get_value (ptem, base, size_table);
+    return a + t * (b - a);
   }
   }
 
 
   bool sanitize (hb_sanitize_context_t *c, const void *base) const
   bool sanitize (hb_sanitize_context_t *c, const void *base) const
@@ -158,45 +190,15 @@ struct trak
 
 
   bool has_data () const { return version.to_int (); }
   bool has_data () const { return version.to_int (); }
 
 
-  bool apply (hb_aat_apply_context_t *c) const
+  hb_position_t get_h_tracking (hb_font_t *font, float track = 0.f) const
+  {
+    float ptem = font->ptem > 0.f ? font->ptem : HB_CORETEXT_DEFAULT_FONT_SIZE;
+    return font->em_scalef_x ((this+horizData).get_tracking (this, ptem, track));
+  }
+  hb_position_t get_v_tracking (hb_font_t *font, float track = 0.f) const
   {
   {
-    TRACE_APPLY (this);
-
-    hb_mask_t trak_mask = c->plan->trak_mask;
-
-    const float ptem = c->font->ptem;
-    if (unlikely (ptem <= 0.f))
-      return_trace (false);
-
-    hb_buffer_t *buffer = c->buffer;
-    if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
-    {
-      const TrackData &trackData = this+horizData;
-      int tracking = trackData.get_tracking (this, ptem);
-      hb_position_t offset_to_add = c->font->em_scalef_x (tracking / 2);
-      hb_position_t advance_to_add = c->font->em_scalef_x (tracking);
-      foreach_grapheme (buffer, start, end)
-      {
-	if (!(buffer->info[start].mask & trak_mask)) continue;
-	buffer->pos[start].x_advance += advance_to_add;
-	buffer->pos[start].x_offset += offset_to_add;
-      }
-    }
-    else
-    {
-      const TrackData &trackData = this+vertData;
-      int tracking = trackData.get_tracking (this, ptem);
-      hb_position_t offset_to_add = c->font->em_scalef_y (tracking / 2);
-      hb_position_t advance_to_add = c->font->em_scalef_y (tracking);
-      foreach_grapheme (buffer, start, end)
-      {
-	if (!(buffer->info[start].mask & trak_mask)) continue;
-	buffer->pos[start].y_advance += advance_to_add;
-	buffer->pos[start].y_offset += offset_to_add;
-      }
-    }
-
-    return_trace (true);
+    float ptem = font->ptem > 0.f ? font->ptem : HB_CORETEXT_DEFAULT_FONT_SIZE;
+    return font->em_scalef_y ((this+vertData).get_tracking (this, ptem, track));
   }
   }
 
 
   bool sanitize (hb_sanitize_context_t *c) const
   bool sanitize (hb_sanitize_context_t *c) const

+ 34 - 12
thirdparty/harfbuzz/src/hb-aat-layout.cc

@@ -34,9 +34,12 @@
 #include "hb-aat-layout-just-table.hh" // Just so we compile it; unused otherwise.
 #include "hb-aat-layout-just-table.hh" // Just so we compile it; unused otherwise.
 #include "hb-aat-layout-kerx-table.hh"
 #include "hb-aat-layout-kerx-table.hh"
 #include "hb-aat-layout-morx-table.hh"
 #include "hb-aat-layout-morx-table.hh"
-#include "hb-aat-layout-trak-table.hh"
+#include "hb-aat-layout-trak-table.hh" // Just so we compile it; unused otherwise.
 #include "hb-aat-ltag-table.hh"
 #include "hb-aat-ltag-table.hh"
 
 
+#include "hb-ot-layout-gsub-table.hh"
+#include "hb-ot-layout-gdef-table.hh"
+
 
 
 /*
 /*
  * hb_aat_apply_context_t
  * hb_aat_apply_context_t
@@ -207,6 +210,36 @@ hb_aat_layout_find_feature_mapping (hb_tag_t tag)
  */
  */
 
 
 
 
+bool
+AAT::morx::is_blocklisted (hb_blob_t *blob,
+                           hb_face_t *face) const
+{
+#ifdef HB_NO_AAT_LAYOUT_BLOCKLIST
+  return false;
+#endif
+
+  switch HB_CODEPOINT_ENCODE3 (blob->length,
+                               face->table.GSUB->table.get_length (),
+                               face->table.GDEF->table.get_length ())
+  {
+    /* https://github.com/harfbuzz/harfbuzz/issues/4108
+       sha1sum:a71ca6813b7e56a772cffff7c24a5166b087197c  AALMAGHRIBI.ttf */
+    case HB_CODEPOINT_ENCODE3 (19892, 2794, 340):
+      return true;
+  }
+  return false;
+}
+
+bool
+AAT::mort::is_blocklisted (hb_blob_t *blob,
+                           hb_face_t *face) const
+{
+#ifdef HB_NO_AAT_LAYOUT_BLOCKLIST
+  return false;
+#endif
+  return false;
+}
+
 void
 void
 hb_aat_layout_compile_map (const hb_aat_map_builder_t *mapper,
 hb_aat_layout_compile_map (const hb_aat_map_builder_t *mapper,
 			   hb_aat_map_t *map)
 			   hb_aat_map_t *map)
@@ -361,17 +394,6 @@ hb_aat_layout_has_tracking (hb_face_t *face)
   return face->table.trak->has_data ();
   return face->table.trak->has_data ();
 }
 }
 
 
-void
-hb_aat_layout_track (const hb_ot_shape_plan_t *plan,
-		     hb_font_t *font,
-		     hb_buffer_t *buffer)
-{
-  const AAT::trak& trak = *font->face->table.trak;
-
-  AAT::hb_aat_apply_context_t c (plan, font, buffer);
-  trak.apply (&c);
-}
-
 /**
 /**
  * hb_aat_layout_get_feature_types:
  * hb_aat_layout_get_feature_types:
  * @face: #hb_face_t to work upon
  * @face: #hb_face_t to work upon

+ 3 - 5
thirdparty/harfbuzz/src/hb-aat-layout.hh

@@ -32,6 +32,9 @@
 #include "hb-ot-shape.hh"
 #include "hb-ot-shape.hh"
 #include "hb-aat-ltag-table.hh"
 #include "hb-aat-ltag-table.hh"
 
 
+/* https://developer.apple.com/documentation/coretext/1508745-ctfontcreatewithgraphicsfont */
+#define HB_CORETEXT_DEFAULT_FONT_SIZE 12.f
+
 struct hb_aat_feature_mapping_t
 struct hb_aat_feature_mapping_t
 {
 {
   hb_tag_t otFeatureTag;
   hb_tag_t otFeatureTag;
@@ -68,10 +71,5 @@ hb_aat_layout_position (const hb_ot_shape_plan_t *plan,
 			hb_font_t *font,
 			hb_font_t *font,
 			hb_buffer_t *buffer);
 			hb_buffer_t *buffer);
 
 
-HB_INTERNAL void
-hb_aat_layout_track (const hb_ot_shape_plan_t *plan,
-		     hb_font_t *font,
-		     hb_buffer_t *buffer);
-
 
 
 #endif /* HB_AAT_LAYOUT_HH */
 #endif /* HB_AAT_LAYOUT_HH */

+ 8 - 7
thirdparty/harfbuzz/src/hb-aat-map.cc

@@ -88,22 +88,23 @@ hb_aat_map_builder_t::compile (hb_aat_map_t  &m)
 
 
   /* Sort features by start/end events. */
   /* Sort features by start/end events. */
   hb_vector_t<feature_event_t> feature_events;
   hb_vector_t<feature_event_t> feature_events;
+  feature_events.alloc_exact (features.length * 2 + 1);
   for (unsigned int i = 0; i < features.length; i++)
   for (unsigned int i = 0; i < features.length; i++)
   {
   {
-    auto &feature = features[i];
+    auto &feature = features.arrayZ[i];
 
 
-    if (features[i].start == features[i].end)
+    if (feature.start == feature.end)
       continue;
       continue;
 
 
     feature_event_t *event;
     feature_event_t *event;
 
 
     event = feature_events.push ();
     event = feature_events.push ();
-    event->index = features[i].start;
+    event->index = feature.start;
     event->start = true;
     event->start = true;
     event->feature = feature.info;
     event->feature = feature.info;
 
 
     event = feature_events.push ();
     event = feature_events.push ();
-    event->index = features[i].end;
+    event->index = feature.end;
     event->start = false;
     event->start = false;
     event->feature = feature.info;
     event->feature = feature.info;
   }
   }
@@ -139,12 +140,12 @@ hb_aat_map_builder_t::compile (hb_aat_map_t  &m)
 	current_features.qsort ();
 	current_features.qsort ();
 	unsigned int j = 0;
 	unsigned int j = 0;
 	for (unsigned int i = 1; i < current_features.length; i++)
 	for (unsigned int i = 1; i < current_features.length; i++)
-	  if (current_features[i].type != current_features[j].type ||
+	  if (current_features.arrayZ[i].type != current_features.arrayZ[j].type ||
 	      /* Nonexclusive feature selectors come in even/odd pairs to turn a setting on/off
 	      /* Nonexclusive feature selectors come in even/odd pairs to turn a setting on/off
 	       * respectively, so we mask out the low-order bit when checking for "duplicates"
 	       * respectively, so we mask out the low-order bit when checking for "duplicates"
 	       * (selectors referring to the same feature setting) here. */
 	       * (selectors referring to the same feature setting) here. */
-	      (!current_features[i].is_exclusive && ((current_features[i].setting & ~1) != (current_features[j].setting & ~1))))
-	    current_features[++j] = current_features[i];
+	      (!current_features.arrayZ[i].is_exclusive && ((current_features.arrayZ[i].setting & ~1) != (current_features.arrayZ[j].setting & ~1))))
+	    current_features.arrayZ[++j] = current_features.arrayZ[i];
 	current_features.shrink (j + 1);
 	current_features.shrink (j + 1);
       }
       }
 
 

+ 5 - 5
thirdparty/harfbuzz/src/hb-algs.hh

@@ -286,7 +286,7 @@ HB_FUNCOBJ (hb_bool);
 
 
 // Compression function for Merkle-Damgard construction.
 // Compression function for Merkle-Damgard construction.
 // This function is generated using the framework provided.
 // This function is generated using the framework provided.
-#define mix(h) (					\
+#define fasthash_mix(h) (					\
 			(void) ((h) ^= (h) >> 23),		\
 			(void) ((h) ^= (h) >> 23),		\
 			(void) ((h) *= 0x2127599bf4325c37ULL),	\
 			(void) ((h) *= 0x2127599bf4325c37ULL),	\
 			(h) ^= (h) >> 47)
 			(h) ^= (h) >> 47)
@@ -310,7 +310,7 @@ static inline uint64_t fasthash64(const void *buf, size_t len, uint64_t seed)
 #pragma GCC diagnostic ignored "-Wcast-align"
 #pragma GCC diagnostic ignored "-Wcast-align"
 	    v  = * (const uint64_t *) (pos++);
 	    v  = * (const uint64_t *) (pos++);
 #pragma GCC diagnostic pop
 #pragma GCC diagnostic pop
-	    h ^= mix(v);
+	    h ^= fasthash_mix(v);
 	    h *= m;
 	    h *= m;
 	  }
 	  }
 	}
 	}
@@ -320,7 +320,7 @@ static inline uint64_t fasthash64(const void *buf, size_t len, uint64_t seed)
 	  while (pos != end)
 	  while (pos != end)
 	  {
 	  {
 	    v  = pos++->v;
 	    v  = pos++->v;
-	    h ^= mix(v);
+	    h ^= fasthash_mix(v);
 	    h *= m;
 	    h *= m;
 	  }
 	  }
 	}
 	}
@@ -336,11 +336,11 @@ static inline uint64_t fasthash64(const void *buf, size_t len, uint64_t seed)
 	case 3: v ^= (uint64_t)pos2[2] << 16; HB_FALLTHROUGH;
 	case 3: v ^= (uint64_t)pos2[2] << 16; HB_FALLTHROUGH;
 	case 2: v ^= (uint64_t)pos2[1] <<  8; HB_FALLTHROUGH;
 	case 2: v ^= (uint64_t)pos2[1] <<  8; HB_FALLTHROUGH;
 	case 1: v ^= (uint64_t)pos2[0];
 	case 1: v ^= (uint64_t)pos2[0];
-		h ^= mix(v);
+		h ^= fasthash_mix(v);
 		h *= m;
 		h *= m;
 	}
 	}
 
 
-	return mix(h);
+	return fasthash_mix(h);
 }
 }
 
 
 static inline uint32_t fasthash32(const void *buf, size_t len, uint32_t seed)
 static inline uint32_t fasthash32(const void *buf, size_t len, uint32_t seed)

+ 2 - 1
thirdparty/harfbuzz/src/hb-array.hh

@@ -251,7 +251,8 @@ struct hb_array_t : hb_iter_with_fallback_t<hb_array_t<Type>, Type&>
     if (end < start + 2)
     if (end < start + 2)
       return;
       return;
 
 
-    for (unsigned lhs = start, rhs = end - 1; lhs < rhs; lhs++, rhs--)
+    unsigned stop = start + (end - start) / 2;
+    for (unsigned lhs = start, rhs = end - 1; lhs < stop; lhs++, rhs--)
       hb_swap (arrayZ[rhs], arrayZ[lhs]);
       hb_swap (arrayZ[rhs], arrayZ[lhs]);
   }
   }
 
 

+ 1 - 0
thirdparty/harfbuzz/src/hb-atomic.hh

@@ -212,6 +212,7 @@ struct hb_atomic_ptr_t
   T *get_acquire () const { return (T *) hb_atomic_ptr_impl_get ((void **) &v); }
   T *get_acquire () const { return (T *) hb_atomic_ptr_impl_get ((void **) &v); }
   bool cmpexch (const T *old, T *new_) const { return hb_atomic_ptr_impl_cmpexch ((void **) &v, (void *) old, (void *) new_); }
   bool cmpexch (const T *old, T *new_) const { return hb_atomic_ptr_impl_cmpexch ((void **) &v, (void *) old, (void *) new_); }
 
 
+  operator bool () const { return get_acquire () != nullptr; }
   T * operator -> () const                    { return get_acquire (); }
   T * operator -> () const                    { return get_acquire (); }
   template <typename C> operator C * () const { return get_acquire (); }
   template <typename C> operator C * () const { return get_acquire (); }
 
 

+ 41 - 14
thirdparty/harfbuzz/src/hb-bit-page.hh

@@ -78,6 +78,28 @@ struct hb_vector_size_t
   hb_vector_size_t operator ~ () const
   hb_vector_size_t operator ~ () const
   { return process (hb_bitwise_neg); }
   { return process (hb_bitwise_neg); }
 
 
+  operator bool () const
+  {
+    for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++)
+      if (v[i])
+	return true;
+    return false;
+  }
+  operator unsigned int () const
+  {
+    unsigned int r = 0;
+    for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++)
+      r += hb_popcount (v[i]);
+    return r;
+  }
+  bool operator == (const hb_vector_size_t &o) const
+  {
+    for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++)
+      if (v[i] != o.v[i])
+	return false;
+    return true;
+  }
+
   hb_array_t<const elt_t> iter () const
   hb_array_t<const elt_t> iter () const
   { return hb_array (v); }
   { return hb_array (v); }
 
 
@@ -89,6 +111,8 @@ struct hb_vector_size_t
 
 
 struct hb_bit_page_t
 struct hb_bit_page_t
 {
 {
+  hb_bit_page_t () { init0 (); }
+
   void init0 () { v.init0 (); population = 0; }
   void init0 () { v.init0 (); population = 0; }
   void init1 () { v.init1 (); population = PAGE_BITS; }
   void init1 () { v.init1 (); population = PAGE_BITS; }
 
 
@@ -101,10 +125,9 @@ struct hb_bit_page_t
   bool is_empty () const
   bool is_empty () const
   {
   {
     if (has_population ()) return !population;
     if (has_population ()) return !population;
-    return
-    + hb_iter (v)
-    | hb_none
-    ;
+    bool empty = !v;
+    if (empty) population = 0;
+    return empty;
   }
   }
   uint32_t hash () const
   uint32_t hash () const
   {
   {
@@ -115,6 +138,10 @@ struct hb_bit_page_t
   void del (hb_codepoint_t g) { elt (g) &= ~mask (g); dirty (); }
   void del (hb_codepoint_t g) { elt (g) &= ~mask (g); dirty (); }
   void set (hb_codepoint_t g, bool value) { if (value) add (g); else del (g); }
   void set (hb_codepoint_t g, bool value) { if (value) add (g); else del (g); }
   bool get (hb_codepoint_t g) const { return elt (g) & mask (g); }
   bool get (hb_codepoint_t g) const { return elt (g) & mask (g); }
+  bool may_have (hb_codepoint_t g) const { return get (g); }
+
+  bool operator [] (hb_codepoint_t g) const { return get (g); }
+  bool operator () (hb_codepoint_t g) const { return get (g); }
 
 
   void add_range (hb_codepoint_t a, hb_codepoint_t b)
   void add_range (hb_codepoint_t a, hb_codepoint_t b)
   {
   {
@@ -220,13 +247,17 @@ struct hb_bit_page_t
   }
   }
 
 
   bool operator == (const hb_bit_page_t &other) const { return is_equal (other); }
   bool operator == (const hb_bit_page_t &other) const { return is_equal (other); }
-  bool is_equal (const hb_bit_page_t &other) const
+  bool is_equal (const hb_bit_page_t &other) const { return v == other.v; }
+  bool intersects (const hb_bit_page_t &other) const
   {
   {
     for (unsigned i = 0; i < len (); i++)
     for (unsigned i = 0; i < len (); i++)
-      if (v[i] != other.v[i])
-	return false;
-    return true;
+      if (v[i] & other.v[i])
+	return true;
+    return false;
   }
   }
+  bool may_intersect (const hb_bit_page_t &other) const
+  { return intersects (other); }
+
   bool operator <= (const hb_bit_page_t &larger_page) const { return is_subset (larger_page); }
   bool operator <= (const hb_bit_page_t &larger_page) const { return is_subset (larger_page); }
   bool is_subset (const hb_bit_page_t &larger_page) const
   bool is_subset (const hb_bit_page_t &larger_page) const
   {
   {
@@ -241,14 +272,10 @@ struct hb_bit_page_t
   }
   }
 
 
   bool has_population () const { return population != UINT_MAX; }
   bool has_population () const { return population != UINT_MAX; }
-  unsigned int get_population () const
+  unsigned get_population () const
   {
   {
     if (has_population ()) return population;
     if (has_population ()) return population;
-    population =
-    + hb_iter (v)
-    | hb_reduce ([] (unsigned pop, const elt_t &_) { return pop + hb_popcount (_); }, 0u)
-    ;
-    return population;
+    return population = v;
   }
   }
 
 
   bool next (hb_codepoint_t *codepoint) const
   bool next (hb_codepoint_t *codepoint) const

+ 4 - 0
thirdparty/harfbuzz/src/hb-bit-set-invertible.hh

@@ -126,6 +126,7 @@ struct hb_bit_set_invertible_t
   { unlikely (inverted) ? (void) s.add_range (a, b) : s.del_range (a, b); }
   { unlikely (inverted) ? (void) s.add_range (a, b) : s.del_range (a, b); }
 
 
   bool get (hb_codepoint_t g) const { return s.get (g) ^ inverted; }
   bool get (hb_codepoint_t g) const { return s.get (g) ^ inverted; }
+  bool may_have (hb_codepoint_t g) const { return get (g); }
 
 
   /* Has interface. */
   /* Has interface. */
   bool operator [] (hb_codepoint_t k) const { return get (k); }
   bool operator [] (hb_codepoint_t k) const { return get (k); }
@@ -139,6 +140,9 @@ struct hb_bit_set_invertible_t
   hb_bit_set_invertible_t& operator << (const hb_codepoint_pair_t& range)
   hb_bit_set_invertible_t& operator << (const hb_codepoint_pair_t& range)
   { add_range (range.first, range.second); return *this; }
   { add_range (range.first, range.second); return *this; }
 
 
+  bool may_intersect (const hb_bit_set_invertible_t &other) const
+  { return inverted || other.inverted || s.intersects (other.s); }
+
   bool intersects (hb_codepoint_t first, hb_codepoint_t last) const
   bool intersects (hb_codepoint_t first, hb_codepoint_t last) const
   {
   {
     hb_codepoint_t c = first - 1;
     hb_codepoint_t c = first - 1;

+ 46 - 19
thirdparty/harfbuzz/src/hb-bit-set.hh

@@ -88,10 +88,11 @@ struct hb_bit_set_t
   {
   {
     if (unlikely (!successful)) return false;
     if (unlikely (!successful)) return false;
 
 
-    if (pages.length == 0 && count == 1)
+    if (pages.length < count && count <= 2)
       exact_size = true; // Most sets are small and local
       exact_size = true; // Most sets are small and local
 
 
-    if (unlikely (!pages.resize (count, clear, exact_size) || !page_map.resize (count, clear, exact_size)))
+    if (unlikely (!pages.resize (count, clear, exact_size) ||
+	!page_map.resize (count, clear)))
     {
     {
       pages.resize (page_map.length, clear, exact_size);
       pages.resize (page_map.length, clear, exact_size);
       successful = false;
       successful = false;
@@ -297,9 +298,9 @@ struct hb_bit_set_t
       unsigned int write_index = 0;
       unsigned int write_index = 0;
       for (unsigned int i = 0; i < page_map.length; i++)
       for (unsigned int i = 0; i < page_map.length; i++)
       {
       {
-	int m = (int) page_map[i].major;
+	int m = (int) page_map.arrayZ[i].major;
 	if (m < ds || de < m)
 	if (m < ds || de < m)
-	  page_map[write_index++] = page_map[i];
+	  page_map.arrayZ[write_index++] = page_map.arrayZ[i];
       }
       }
       compact (compact_workspace, write_index);
       compact (compact_workspace, write_index);
       resize (write_index);
       resize (write_index);
@@ -345,6 +346,7 @@ struct hb_bit_set_t
       return false;
       return false;
     return page->get (g);
     return page->get (g);
   }
   }
+  bool may_have (hb_codepoint_t g) const { return get (g); }
 
 
   /* Has interface. */
   /* Has interface. */
   bool operator [] (hb_codepoint_t k) const { return get (k); }
   bool operator [] (hb_codepoint_t k) const { return get (k); }
@@ -358,6 +360,31 @@ struct hb_bit_set_t
   hb_bit_set_t& operator << (const hb_codepoint_pair_t& range)
   hb_bit_set_t& operator << (const hb_codepoint_pair_t& range)
   { add_range (range.first, range.second); return *this; }
   { add_range (range.first, range.second); return *this; }
 
 
+  bool intersects (const hb_bit_set_t &other) const
+  {
+    unsigned int na = pages.length;
+    unsigned int nb = other.pages.length;
+
+    unsigned int a = 0, b = 0;
+    for (; a < na && b < nb; )
+    {
+      if (page_map.arrayZ[a].major == other.page_map.arrayZ[b].major)
+      {
+	if (page_at (a).intersects (other.page_at (b)))
+	  return true;
+	a++;
+	b++;
+      }
+      else if (page_map.arrayZ[a].major < other.page_map.arrayZ[b].major)
+	a++;
+      else
+	b++;
+    }
+    return false;
+  }
+  bool may_intersect (const hb_bit_set_t &other) const
+  { return intersects (other); }
+
   bool intersects (hb_codepoint_t first, hb_codepoint_t last) const
   bool intersects (hb_codepoint_t first, hb_codepoint_t last) const
   {
   {
     hb_codepoint_t c = first - 1;
     hb_codepoint_t c = first - 1;
@@ -389,7 +416,7 @@ struct hb_bit_set_t
     {
     {
       if (page_at (a).is_empty ()) { a++; continue; }
       if (page_at (a).is_empty ()) { a++; continue; }
       if (other.page_at (b).is_empty ()) { b++; continue; }
       if (other.page_at (b).is_empty ()) { b++; continue; }
-      if (page_map[a].major != other.page_map[b].major ||
+      if (page_map.arrayZ[a].major != other.page_map.arrayZ[b].major ||
 	  !page_at (a).is_equal (other.page_at (b)))
 	  !page_at (a).is_equal (other.page_at (b)))
 	return false;
 	return false;
       a++;
       a++;
@@ -412,8 +439,8 @@ struct hb_bit_set_t
     uint32_t spi = 0;
     uint32_t spi = 0;
     for (uint32_t lpi = 0; spi < page_map.length && lpi < larger_set.page_map.length; lpi++)
     for (uint32_t lpi = 0; spi < page_map.length && lpi < larger_set.page_map.length; lpi++)
     {
     {
-      uint32_t spm = page_map[spi].major;
-      uint32_t lpm = larger_set.page_map[lpi].major;
+      uint32_t spm = page_map.arrayZ[spi].major;
+      uint32_t lpm = larger_set.page_map.arrayZ[lpi].major;
       auto sp = page_at (spi);
       auto sp = page_at (spi);
 
 
       if (spm < lpm && !sp.is_empty ())
       if (spm < lpm && !sp.is_empty ())
@@ -503,7 +530,7 @@ struct hb_bit_set_t
 
 
     for (; a < na && b < nb; )
     for (; a < na && b < nb; )
     {
     {
-      if (page_map[a].major == other.page_map[b].major)
+      if (page_map.arrayZ[a].major == other.page_map.arrayZ[b].major)
       {
       {
 	if (!passthru_left)
 	if (!passthru_left)
 	{
 	{
@@ -512,7 +539,7 @@ struct hb_bit_set_t
 	  // passthru_left is set since no left side pages will be removed
 	  // passthru_left is set since no left side pages will be removed
 	  // in that case.
 	  // in that case.
 	  if (write_index < a)
 	  if (write_index < a)
-	    page_map[write_index] = page_map[a];
+	    page_map.arrayZ[write_index] = page_map.arrayZ[a];
 	  write_index++;
 	  write_index++;
 	}
 	}
 
 
@@ -520,7 +547,7 @@ struct hb_bit_set_t
 	a++;
 	a++;
 	b++;
 	b++;
       }
       }
-      else if (page_map[a].major < other.page_map[b].major)
+      else if (page_map.arrayZ[a].major < other.page_map.arrayZ[b].major)
       {
       {
 	if (passthru_left)
 	if (passthru_left)
 	  count++;
 	  count++;
@@ -765,8 +792,8 @@ struct hb_bit_set_t
     unsigned int initial_size = size;
     unsigned int initial_size = size;
     for (unsigned int i = start_page; i < page_map.length && size; i++)
     for (unsigned int i = start_page; i < page_map.length && size; i++)
     {
     {
-      uint32_t base = major_start (page_map[i].major);
-      unsigned int n = pages[page_map[i].index].write (base, start_page_value, out, size);
+      uint32_t base = major_start (page_map.arrayZ[i].major);
+      unsigned int n = pages[page_map.arrayZ[i].index].write (base, start_page_value, out, size);
       out += n;
       out += n;
       size -= n;
       size -= n;
       start_page_value = 0;
       start_page_value = 0;
@@ -814,8 +841,8 @@ struct hb_bit_set_t
     hb_codepoint_t next_value = codepoint + 1;
     hb_codepoint_t next_value = codepoint + 1;
     for (unsigned int i=start_page; i<page_map.length && size; i++)
     for (unsigned int i=start_page; i<page_map.length && size; i++)
     {
     {
-      uint32_t base = major_start (page_map[i].major);
-      unsigned int n = pages[page_map[i].index].write_inverted (base, start_page_value, out, size, &next_value);
+      uint32_t base = major_start (page_map.arrayZ[i].major);
+      unsigned int n = pages[page_map.arrayZ[i].index].write_inverted (base, start_page_value, out, size, &next_value);
       out += n;
       out += n;
       size -= n;
       size -= n;
       start_page_value = 0;
       start_page_value = 0;
@@ -846,8 +873,8 @@ struct hb_bit_set_t
     unsigned count = pages.length;
     unsigned count = pages.length;
     for (unsigned i = 0; i < count; i++)
     for (unsigned i = 0; i < count; i++)
     {
     {
-      const auto& map = page_map[i];
-      const auto& page = pages[map.index];
+      const auto& map = page_map.arrayZ[i];
+      const auto& page = pages.arrayZ[map.index];
 
 
       if (!page.is_empty ())
       if (!page.is_empty ())
 	return map.major * page_t::PAGE_BITS + page.get_min ();
 	return map.major * page_t::PAGE_BITS + page.get_min ();
@@ -859,8 +886,8 @@ struct hb_bit_set_t
     unsigned count = pages.length;
     unsigned count = pages.length;
     for (signed i = count - 1; i >= 0; i--)
     for (signed i = count - 1; i >= 0; i--)
     {
     {
-      const auto& map = page_map[(unsigned) i];
-      const auto& page = pages[map.index];
+      const auto& map = page_map.arrayZ[(unsigned) i];
+      const auto& page = pages.arrayZ[map.index];
 
 
       if (!page.is_empty ())
       if (!page.is_empty ())
 	return map.major * page_t::PAGE_BITS + page.get_max ();
 	return map.major * page_t::PAGE_BITS + page.get_max ();
@@ -961,7 +988,7 @@ struct hb_bit_set_t
       return nullptr;
       return nullptr;
 
 
     last_page_lookup = i;
     last_page_lookup = i;
-    return &pages.arrayZ[page_map[i].index];
+    return &pages.arrayZ[page_map.arrayZ[i].index];
   }
   }
   page_t &page_at (unsigned int i)
   page_t &page_at (unsigned int i)
   {
   {

+ 222 - 212
thirdparty/harfbuzz/src/hb-buffer-deserialize-json.hh

@@ -34,36 +34,36 @@
 
 
 #line 36 "hb-buffer-deserialize-json.hh"
 #line 36 "hb-buffer-deserialize-json.hh"
 static const unsigned char _deserialize_json_trans_keys[] = {
 static const unsigned char _deserialize_json_trans_keys[] = {
-	0u, 0u, 9u, 123u, 9u, 34u, 97u, 117u, 120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, 
-	48u, 57u, 9u, 125u, 9u, 125u, 9u, 93u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 
+	0u, 0u, 9u, 123u, 9u, 123u, 9u, 34u, 97u, 117u, 120u, 121u, 34u, 34u, 9u, 58u, 
+	9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 
 	48u, 57u, 9u, 125u, 9u, 125u, 108u, 108u, 34u, 34u, 9u, 58u, 9u, 57u, 9u, 125u, 
 	48u, 57u, 9u, 125u, 9u, 125u, 108u, 108u, 34u, 34u, 9u, 58u, 9u, 57u, 9u, 125u, 
 	9u, 125u, 120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 
 	9u, 125u, 120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 
 	34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 108u, 108u, 34u, 34u, 
 	34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 108u, 108u, 34u, 34u, 
 	9u, 58u, 9u, 57u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 34u, 92u, 
 	9u, 58u, 9u, 57u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 34u, 92u, 
 	9u, 125u, 34u, 92u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 9u, 125u, 
 	9u, 125u, 34u, 92u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 9u, 125u, 
-	9u, 123u, 0u, 0u, 0
+	9u, 93u, 9u, 123u, 0u, 0u, 0
 };
 };
 
 
 static const char _deserialize_json_key_spans[] = {
 static const char _deserialize_json_key_spans[] = {
-	0, 115, 26, 21, 2, 1, 50, 49, 
-	10, 117, 117, 85, 117, 1, 50, 49, 
+	0, 115, 115, 26, 21, 2, 1, 50, 
+	49, 10, 117, 117, 117, 1, 50, 49, 
 	10, 117, 117, 1, 1, 50, 49, 117, 
 	10, 117, 117, 1, 1, 50, 49, 117, 
 	117, 2, 1, 50, 49, 10, 117, 117, 
 	117, 2, 1, 50, 49, 10, 117, 117, 
 	1, 50, 49, 10, 117, 117, 1, 1, 
 	1, 50, 49, 10, 117, 117, 1, 1, 
 	50, 49, 117, 117, 1, 50, 49, 59, 
 	50, 49, 117, 117, 1, 50, 49, 59, 
 	117, 59, 117, 117, 1, 50, 49, 117, 
 	117, 59, 117, 117, 1, 50, 49, 117, 
-	115, 0
+	85, 115, 0
 };
 };
 
 
 static const short _deserialize_json_index_offsets[] = {
 static const short _deserialize_json_index_offsets[] = {
-	0, 0, 116, 143, 165, 168, 170, 221, 
-	271, 282, 400, 518, 604, 722, 724, 775, 
-	825, 836, 954, 1072, 1074, 1076, 1127, 1177, 
-	1295, 1413, 1416, 1418, 1469, 1519, 1530, 1648, 
-	1766, 1768, 1819, 1869, 1880, 1998, 2116, 2118, 
-	2120, 2171, 2221, 2339, 2457, 2459, 2510, 2560, 
-	2620, 2738, 2798, 2916, 3034, 3036, 3087, 3137, 
-	3255, 3371
+	0, 0, 116, 232, 259, 281, 284, 286, 
+	337, 387, 398, 516, 634, 752, 754, 805, 
+	855, 866, 984, 1102, 1104, 1106, 1157, 1207, 
+	1325, 1443, 1446, 1448, 1499, 1549, 1560, 1678, 
+	1796, 1798, 1849, 1899, 1910, 2028, 2146, 2148, 
+	2150, 2201, 2251, 2369, 2487, 2489, 2540, 2590, 
+	2650, 2768, 2828, 2946, 3064, 3066, 3117, 3167, 
+	3285, 3371, 3487
 };
 };
 
 
 static const char _deserialize_json_indicies[] = {
 static const char _deserialize_json_indicies[] = {
@@ -77,51 +77,51 @@ static const char _deserialize_json_indicies[] = {
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 2, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 3, 1, 2, 2, 2, 
+	2, 2, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 2, 1, 3, 3, 3, 
-	3, 3, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 2, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 3, 1, 4, 1, 
-	5, 1, 6, 7, 1, 8, 9, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 10, 1, 11, 12, 
-	1, 13, 1, 13, 13, 13, 13, 13, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 13, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 14, 1, 14, 14, 
-	14, 14, 14, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 14, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 15, 1, 1, 16, 17, 17, 
-	17, 17, 17, 17, 17, 17, 17, 1, 
-	18, 19, 19, 19, 19, 19, 19, 19, 
-	19, 19, 1, 20, 20, 20, 20, 20, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 20, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 21, 1, 
+	1, 1, 1, 1, 1, 1, 1, 3, 
+	1, 4, 4, 4, 4, 4, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	4, 1, 5, 1, 6, 1, 7, 8, 
+	1, 9, 10, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	11, 1, 12, 13, 1, 14, 1, 14, 
+	14, 14, 14, 14, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 14, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	15, 1, 15, 15, 15, 15, 15, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 22, 
-	1, 23, 23, 23, 23, 23, 1, 1, 
+	1, 15, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 16, 1, 
+	1, 17, 18, 18, 18, 18, 18, 18, 
+	18, 18, 18, 1, 19, 20, 20, 20, 
+	20, 20, 20, 20, 20, 20, 1, 21, 
+	21, 21, 21, 21, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 21, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	23, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 3, 1, 1, 1, 
+	1, 1, 22, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
@@ -131,94 +131,99 @@ static const char _deserialize_json_indicies[] = {
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 24, 1, 25, 
-	25, 25, 25, 25, 1, 1, 1, 1, 
+	1, 1, 1, 23, 1, 24, 24, 24, 
+	24, 24, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 25, 1, 
+	1, 1, 1, 1, 24, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 26, 1, 1, 1, 1, 1, 
+	4, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 27, 1, 20, 20, 20, 
-	20, 20, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 20, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	21, 1, 1, 1, 19, 19, 19, 19, 
-	19, 19, 19, 19, 19, 19, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 25, 1, 21, 21, 21, 21, 21, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 21, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 22, 1, 
+	1, 1, 20, 20, 20, 20, 20, 20, 
+	20, 20, 20, 20, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 22, 1, 28, 1, 28, 28, 28, 
-	28, 28, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 28, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 29, 1, 
-	29, 29, 29, 29, 29, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 23, 
+	1, 26, 1, 26, 26, 26, 26, 26, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 29, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 30, 1, 1, 31, 
-	32, 32, 32, 32, 32, 32, 32, 32, 
-	32, 1, 33, 34, 34, 34, 34, 34, 
-	34, 34, 34, 34, 1, 35, 35, 35, 
-	35, 35, 1, 1, 1, 1, 1, 1, 
+	1, 1, 26, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 35, 1, 1, 1, 
+	1, 1, 1, 1, 27, 1, 27, 27, 
+	27, 27, 27, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	36, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 27, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 28, 1, 1, 29, 30, 30, 
+	30, 30, 30, 30, 30, 30, 30, 1, 
+	31, 32, 32, 32, 32, 32, 32, 32, 
+	32, 32, 1, 33, 33, 33, 33, 33, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 33, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 34, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 37, 1, 35, 35, 35, 35, 35, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 35, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 36, 1, 
-	1, 1, 34, 34, 34, 34, 34, 34, 
-	34, 34, 34, 34, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 35, 
+	1, 33, 33, 33, 33, 33, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	33, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 34, 1, 1, 1, 
+	32, 32, 32, 32, 32, 32, 32, 32, 
+	32, 32, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 37, 
-	1, 38, 1, 39, 1, 39, 39, 39, 
-	39, 39, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 39, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 40, 1, 
-	40, 40, 40, 40, 40, 1, 1, 1, 
+	1, 1, 1, 1, 1, 35, 1, 36, 
+	1, 37, 1, 37, 37, 37, 37, 37, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 40, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 37, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 38, 1, 38, 38, 
+	38, 38, 38, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 38, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 39, 40, 40, 
+	40, 40, 40, 40, 40, 40, 40, 1, 
+	41, 41, 41, 41, 41, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 41, 
 	1, 1, 1, 1, 1, 1, 1, 41, 
-	42, 42, 42, 42, 42, 42, 42, 42, 
-	42, 1, 43, 43, 43, 43, 43, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 42, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 43, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 44, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
@@ -227,15 +232,14 @@ static const char _deserialize_json_indicies[] = {
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 43, 1, 41, 41, 
+	41, 41, 41, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 45, 1, 
-	43, 43, 43, 43, 43, 1, 1, 1, 
+	1, 1, 1, 1, 1, 41, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 43, 
+	1, 42, 1, 1, 1, 44, 44, 44, 
+	44, 44, 44, 44, 44, 44, 44, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 44, 1, 1, 1, 46, 
-	46, 46, 46, 46, 46, 46, 46, 46, 
-	46, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
@@ -243,26 +247,26 @@ static const char _deserialize_json_indicies[] = {
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 45, 1, 47, 48, 
-	1, 49, 1, 49, 49, 49, 49, 49, 
+	1, 1, 43, 1, 45, 46, 1, 47, 
+	1, 47, 47, 47, 47, 47, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 49, 1, 1, 1, 1, 1, 
+	47, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 50, 1, 50, 50, 
-	50, 50, 50, 1, 1, 1, 1, 1, 
+	1, 1, 48, 1, 48, 48, 48, 48, 
+	48, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 50, 1, 1, 
+	1, 1, 1, 48, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 51, 1, 1, 52, 53, 53, 
-	53, 53, 53, 53, 53, 53, 53, 1, 
-	54, 55, 55, 55, 55, 55, 55, 55, 
-	55, 55, 1, 56, 56, 56, 56, 56, 
+	49, 1, 1, 50, 51, 51, 51, 51, 
+	51, 51, 51, 51, 51, 1, 52, 53, 
+	53, 53, 53, 53, 53, 53, 53, 53, 
+	1, 54, 54, 54, 54, 54, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 56, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 57, 1, 
+	54, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 55, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
@@ -272,14 +276,13 @@ static const char _deserialize_json_indicies[] = {
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 58, 
-	1, 56, 56, 56, 56, 56, 1, 1, 
+	1, 1, 1, 1, 1, 56, 1, 54, 
+	54, 54, 54, 54, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 54, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	56, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 57, 1, 1, 1, 
-	55, 55, 55, 55, 55, 55, 55, 55, 
-	55, 55, 1, 1, 1, 1, 1, 1, 
+	1, 1, 55, 1, 1, 1, 53, 53, 
+	53, 53, 53, 53, 53, 53, 53, 53, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
@@ -287,119 +290,120 @@ static const char _deserialize_json_indicies[] = {
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 58, 1, 59, 
-	1, 59, 59, 59, 59, 59, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 56, 1, 57, 1, 57, 
+	57, 57, 57, 57, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	59, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 57, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	58, 1, 58, 58, 58, 58, 58, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 58, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 59, 1, 
+	1, 60, 61, 61, 61, 61, 61, 61, 
+	61, 61, 61, 1, 62, 63, 63, 63, 
+	63, 63, 63, 63, 63, 63, 1, 64, 
+	64, 64, 64, 64, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 64, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 60, 1, 60, 60, 60, 60, 
-	60, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 65, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 60, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	61, 1, 1, 62, 63, 63, 63, 63, 
-	63, 63, 63, 63, 63, 1, 64, 65, 
-	65, 65, 65, 65, 65, 65, 65, 65, 
-	1, 66, 66, 66, 66, 66, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	66, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 67, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 66, 1, 64, 64, 64, 
+	64, 64, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 64, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	65, 1, 1, 1, 63, 63, 63, 63, 
+	63, 63, 63, 63, 63, 63, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 68, 1, 66, 
-	66, 66, 66, 66, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 66, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 67, 1, 1, 1, 65, 65, 
-	65, 65, 65, 65, 65, 65, 65, 65, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 66, 1, 67, 1, 68, 1, 68, 
+	68, 68, 68, 68, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 68, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 68, 1, 69, 1, 70, 
-	1, 70, 70, 70, 70, 70, 1, 1, 
+	69, 1, 69, 69, 69, 69, 69, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	70, 1, 1, 1, 1, 1, 1, 1, 
+	1, 69, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 70, 71, 71, 71, 71, 71, 71, 
+	71, 71, 71, 1, 72, 72, 72, 72, 
+	72, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 71, 1, 71, 71, 71, 71, 
-	71, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 72, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 73, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 71, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 72, 73, 73, 73, 73, 
-	73, 73, 73, 73, 73, 1, 74, 74, 
-	74, 74, 74, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 74, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 75, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	74, 1, 72, 72, 72, 72, 72, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 72, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 73, 1, 1, 
+	1, 75, 75, 75, 75, 75, 75, 75, 
+	75, 75, 75, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 76, 1, 74, 74, 74, 74, 
-	74, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 74, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 75, 
-	1, 1, 1, 77, 77, 77, 77, 77, 
-	77, 77, 77, 77, 77, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 74, 1, 
+	76, 1, 76, 76, 76, 76, 76, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 76, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	76, 1, 78, 1, 78, 78, 78, 78, 
-	78, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 78, 1, 1, 1, 1, 
+	1, 1, 1, 77, 1, 77, 77, 77, 
+	77, 77, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 77, 1, 78, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 79, 1, 79, 
-	79, 79, 79, 79, 1, 1, 1, 1, 
+	1, 1, 1, 1, 79, 80, 80, 80, 
+	80, 80, 80, 80, 80, 80, 1, 82, 
+	81, 81, 81, 81, 81, 81, 81, 81, 
+	81, 81, 81, 81, 81, 81, 81, 81, 
+	81, 81, 81, 81, 81, 81, 81, 81, 
+	81, 81, 81, 81, 81, 81, 81, 81, 
+	81, 81, 81, 81, 81, 81, 81, 81, 
+	81, 81, 81, 81, 81, 81, 81, 81, 
+	81, 81, 81, 81, 81, 81, 81, 81, 
+	81, 83, 81, 84, 84, 84, 84, 84, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 79, 1, 
-	80, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 81, 82, 
-	82, 82, 82, 82, 82, 82, 82, 82, 
-	1, 84, 83, 83, 83, 83, 83, 83, 
-	83, 83, 83, 83, 83, 83, 83, 83, 
-	83, 83, 83, 83, 83, 83, 83, 83, 
-	83, 83, 83, 83, 83, 83, 83, 83, 
-	83, 83, 83, 83, 83, 83, 83, 83, 
-	83, 83, 83, 83, 83, 83, 83, 83, 
-	83, 83, 83, 83, 83, 83, 83, 83, 
-	83, 83, 83, 85, 83, 86, 86, 86, 
-	86, 86, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 86, 1, 1, 1, 
+	1, 1, 84, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 85, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	87, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
@@ -408,20 +412,21 @@ static const char _deserialize_json_indicies[] = {
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 86, 
+	1, 81, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 88, 1, 83, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 81, 1, 87, 87, 87, 
+	87, 87, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 83, 1, 89, 
-	89, 89, 89, 89, 1, 1, 1, 1, 
+	1, 1, 1, 1, 87, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 89, 1, 
+	88, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 90, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
@@ -430,97 +435,107 @@ static const char _deserialize_json_indicies[] = {
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 89, 1, 87, 87, 87, 87, 87, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 91, 1, 89, 89, 89, 
-	89, 89, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 89, 1, 1, 1, 
+	1, 1, 87, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 88, 1, 
+	1, 1, 90, 90, 90, 90, 90, 90, 
+	90, 90, 90, 90, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	90, 1, 1, 1, 92, 92, 92, 92, 
-	92, 92, 92, 92, 92, 92, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 89, 
+	1, 91, 1, 91, 91, 91, 91, 91, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 91, 1, 93, 1, 93, 93, 93, 
-	93, 93, 1, 1, 1, 1, 1, 1, 
+	1, 1, 91, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 93, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 92, 1, 92, 92, 
+	92, 92, 92, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 94, 1, 
-	94, 94, 94, 94, 94, 1, 1, 1, 
+	1, 1, 1, 1, 1, 92, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 94, 
+	1, 1, 1, 1, 1, 93, 94, 94, 
+	94, 94, 94, 94, 94, 94, 94, 1, 
+	87, 87, 87, 87, 87, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 95, 
-	96, 96, 96, 96, 96, 96, 96, 96, 
-	96, 1, 89, 89, 89, 89, 89, 1, 
+	1, 1, 1, 1, 1, 1, 1, 87, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 88, 1, 1, 1, 95, 
+	95, 95, 95, 95, 95, 95, 95, 95, 
+	95, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 89, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 90, 1, 1, 
-	1, 97, 97, 97, 97, 97, 97, 97, 
-	97, 97, 97, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 89, 1, 96, 96, 
+	96, 96, 96, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 91, 1, 
-	0, 0, 0, 0, 0, 1, 1, 1, 
+	1, 1, 1, 1, 1, 96, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 0, 
+	1, 97, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 98, 1, 2, 2, 2, 2, 
+	2, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 2, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 2, 1, 1, 0
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 3, 1, 
+	1, 0
 };
 };
 
 
 static const char _deserialize_json_trans_targs[] = {
 static const char _deserialize_json_trans_targs[] = {
-	1, 0, 2, 2, 3, 4, 19, 25, 
-	38, 44, 52, 5, 13, 6, 7, 8, 
-	9, 12, 9, 12, 10, 2, 11, 10, 
-	11, 11, 56, 57, 14, 15, 16, 17, 
-	18, 17, 18, 10, 2, 11, 20, 21, 
-	22, 23, 24, 10, 2, 11, 24, 26, 
-	32, 27, 28, 29, 30, 31, 30, 31, 
-	10, 2, 11, 33, 34, 35, 36, 37, 
-	36, 37, 10, 2, 11, 39, 40, 41, 
-	42, 43, 10, 2, 11, 43, 45, 46, 
-	47, 50, 51, 47, 48, 49, 10, 2, 
-	11, 10, 2, 11, 51, 53, 54, 50, 
-	55, 55
+	1, 0, 2, 3, 3, 4, 5, 19, 
+	25, 38, 44, 52, 6, 13, 7, 8, 
+	9, 10, 12, 10, 12, 11, 3, 56, 
+	11, 56, 14, 15, 16, 17, 18, 17, 
+	18, 11, 3, 56, 20, 21, 22, 23, 
+	24, 11, 3, 56, 24, 26, 32, 27, 
+	28, 29, 30, 31, 30, 31, 11, 3, 
+	56, 33, 34, 35, 36, 37, 36, 37, 
+	11, 3, 56, 39, 40, 41, 42, 43, 
+	11, 3, 56, 43, 45, 46, 47, 50, 
+	51, 47, 48, 49, 11, 3, 56, 11, 
+	3, 56, 51, 53, 54, 50, 55, 55, 
+	56, 57, 58
 };
 };
 
 
 static const char _deserialize_json_trans_actions[] = {
 static const char _deserialize_json_trans_actions[] = {
-	0, 0, 1, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 2, 
-	2, 2, 0, 0, 3, 3, 4, 0, 
-	5, 0, 0, 0, 0, 0, 2, 2, 
-	2, 0, 0, 6, 6, 7, 0, 0, 
-	0, 2, 2, 8, 8, 9, 0, 0, 
-	0, 0, 0, 2, 2, 2, 0, 0, 
-	10, 10, 11, 0, 0, 2, 2, 2, 
-	0, 0, 12, 12, 13, 0, 0, 0, 
-	2, 2, 14, 14, 15, 0, 0, 0, 
-	2, 16, 16, 0, 17, 0, 18, 18, 
-	19, 20, 20, 21, 17, 0, 0, 22, 
-	22, 23
+	0, 0, 0, 1, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	2, 2, 2, 0, 0, 3, 3, 4, 
+	0, 5, 0, 0, 2, 2, 2, 0, 
+	0, 6, 6, 7, 0, 0, 0, 2, 
+	2, 8, 8, 9, 0, 0, 0, 0, 
+	0, 2, 2, 2, 0, 0, 10, 10, 
+	11, 0, 0, 2, 2, 2, 0, 0, 
+	12, 12, 13, 0, 0, 0, 2, 2, 
+	14, 14, 15, 0, 0, 0, 2, 16, 
+	16, 0, 17, 0, 18, 18, 19, 20, 
+	20, 21, 17, 0, 0, 22, 22, 23, 
+	0, 0, 0
 };
 };
 
 
 static const int deserialize_json_start = 1;
 static const int deserialize_json_start = 1;
@@ -545,22 +560,17 @@ _hb_buffer_deserialize_json (hb_buffer_t *buffer,
   /* Ensure we have positions. */
   /* Ensure we have positions. */
   (void) hb_buffer_get_glyph_positions (buffer, nullptr);
   (void) hb_buffer_get_glyph_positions (buffer, nullptr);
 
 
-  while (p < pe && ISSPACE (*p))
-    p++;
-  if (p < pe && *p == (buffer->len ? ',' : '['))
-    *end_ptr = ++p;
-
   const char *tok = nullptr;
   const char *tok = nullptr;
   int cs;
   int cs;
   hb_glyph_info_t info = {0};
   hb_glyph_info_t info = {0};
   hb_glyph_position_t pos = {0};
   hb_glyph_position_t pos = {0};
   
   
-#line 559 "hb-buffer-deserialize-json.hh"
+#line 569 "hb-buffer-deserialize-json.hh"
 	{
 	{
 	cs = deserialize_json_start;
 	cs = deserialize_json_start;
 	}
 	}
 
 
-#line 564 "hb-buffer-deserialize-json.hh"
+#line 574 "hb-buffer-deserialize-json.hh"
 	{
 	{
 	int _slen;
 	int _slen;
 	int _trans;
 	int _trans;
@@ -772,7 +782,7 @@ _resume:
 	*end_ptr = p;
 	*end_ptr = p;
 }
 }
 	break;
 	break;
-#line 776 "hb-buffer-deserialize-json.hh"
+#line 786 "hb-buffer-deserialize-json.hh"
 	}
 	}
 
 
 _again:
 _again:
@@ -784,7 +794,7 @@ _again:
 	_out: {}
 	_out: {}
 	}
 	}
 
 
-#line 137 "hb-buffer-deserialize-json.rl"
+#line 132 "hb-buffer-deserialize-json.rl"
 
 
 
 
   *end_ptr = p;
   *end_ptr = p;

+ 4 - 4
thirdparty/harfbuzz/src/hb-buffer.cc

@@ -860,7 +860,7 @@ hb_buffer_destroy (hb_buffer_t *buffer)
  * @destroy: (nullable): A callback to call when @data is not needed anymore
  * @destroy: (nullable): A callback to call when @data is not needed anymore
  * @replace: Whether to replace an existing data with the same key
  * @replace: Whether to replace an existing data with the same key
  *
  *
- * Attaches a user-data key/data pair to the specified buffer. 
+ * Attaches a user-data key/data pair to the specified buffer.
  *
  *
  * Return value: `true` if success, `false` otherwise
  * Return value: `true` if success, `false` otherwise
  *
  *
@@ -1209,7 +1209,7 @@ hb_buffer_get_flags (const hb_buffer_t *buffer)
  * @cluster_level: The cluster level to set on the buffer
  * @cluster_level: The cluster level to set on the buffer
  *
  *
  * Sets the cluster level of a buffer. The #hb_buffer_cluster_level_t
  * Sets the cluster level of a buffer. The #hb_buffer_cluster_level_t
- * dictates one aspect of how HarfBuzz will treat non-base characters 
+ * dictates one aspect of how HarfBuzz will treat non-base characters
  * during shaping.
  * during shaping.
  *
  *
  * Since: 0.9.42
  * Since: 0.9.42
@@ -1229,7 +1229,7 @@ hb_buffer_set_cluster_level (hb_buffer_t               *buffer,
  * @buffer: An #hb_buffer_t
  * @buffer: An #hb_buffer_t
  *
  *
  * Fetches the cluster level of a buffer. The #hb_buffer_cluster_level_t
  * Fetches the cluster level of a buffer. The #hb_buffer_cluster_level_t
- * dictates one aspect of how HarfBuzz will treat non-base characters 
+ * dictates one aspect of how HarfBuzz will treat non-base characters
  * during shaping.
  * during shaping.
  *
  *
  * Return value: The cluster level of @buffer
  * Return value: The cluster level of @buffer
@@ -1983,7 +1983,7 @@ hb_buffer_add_codepoints (hb_buffer_t          *buffer,
  * @buffer: An #hb_buffer_t
  * @buffer: An #hb_buffer_t
  * @source: source #hb_buffer_t
  * @source: source #hb_buffer_t
  * @start: start index into source buffer to copy.  Use 0 to copy from start of buffer.
  * @start: start index into source buffer to copy.  Use 0 to copy from start of buffer.
- * @end: end index into source buffer to copy.  Use @HB_FEATURE_GLOBAL_END to copy to end of buffer.
+ * @end: end index into source buffer to copy.  Use @UINT_MAX (or ((unsigned int) -1)) to copy to end of buffer.
  *
  *
  * Append (part of) contents of another buffer to this buffer.
  * Append (part of) contents of another buffer to this buffer.
  *
  *

+ 9 - 8
thirdparty/harfbuzz/src/hb-buffer.hh

@@ -32,7 +32,6 @@
 
 
 #include "hb.hh"
 #include "hb.hh"
 #include "hb-unicode.hh"
 #include "hb-unicode.hh"
-#include "hb-set-digest.hh"
 
 
 
 
 static_assert ((sizeof (hb_glyph_info_t) == 20), "");
 static_assert ((sizeof (hb_glyph_info_t) == 20), "");
@@ -182,22 +181,24 @@ struct hb_buffer_t
     allocated_var_bits = 0;
     allocated_var_bits = 0;
   }
   }
 
 
+  HB_ALWAYS_INLINE
   hb_glyph_info_t &cur (unsigned int i = 0) { return info[idx + i]; }
   hb_glyph_info_t &cur (unsigned int i = 0) { return info[idx + i]; }
+  HB_ALWAYS_INLINE
   hb_glyph_info_t cur (unsigned int i = 0) const { return info[idx + i]; }
   hb_glyph_info_t cur (unsigned int i = 0) const { return info[idx + i]; }
 
 
+  HB_ALWAYS_INLINE
   hb_glyph_position_t &cur_pos (unsigned int i = 0) { return pos[idx + i]; }
   hb_glyph_position_t &cur_pos (unsigned int i = 0) { return pos[idx + i]; }
+  HB_ALWAYS_INLINE
   hb_glyph_position_t cur_pos (unsigned int i = 0) const { return pos[idx + i]; }
   hb_glyph_position_t cur_pos (unsigned int i = 0) const { return pos[idx + i]; }
 
 
+  HB_ALWAYS_INLINE
   hb_glyph_info_t &prev ()      { return out_info[out_len ? out_len - 1 : 0]; }
   hb_glyph_info_t &prev ()      { return out_info[out_len ? out_len - 1 : 0]; }
+  HB_ALWAYS_INLINE
   hb_glyph_info_t prev () const { return out_info[out_len ? out_len - 1 : 0]; }
   hb_glyph_info_t prev () const { return out_info[out_len ? out_len - 1 : 0]; }
 
 
-  hb_set_digest_t digest () const
-  {
-    hb_set_digest_t d;
-    d.init ();
-    d.add_array (&info[0].codepoint, len, sizeof (info[0]));
-    return d;
-  }
+  template <typename set_t>
+  void collect_codepoints (set_t &d) const
+  { d.clear (); d.add_array (&info[0].codepoint, len, sizeof (info[0])); }
 
 
   HB_INTERNAL void similar (const hb_buffer_t &src);
   HB_INTERNAL void similar (const hb_buffer_t &src);
   HB_INTERNAL void reset ();
   HB_INTERNAL void reset ();

+ 9 - 3
thirdparty/harfbuzz/src/hb-cairo.cc

@@ -180,7 +180,7 @@ hb_cairo_paint_color_glyph (hb_paint_funcs_t *pfuncs HB_UNUSED,
 
 
   hb_position_t x_scale, y_scale;
   hb_position_t x_scale, y_scale;
   hb_font_get_scale (font, &x_scale, &y_scale);
   hb_font_get_scale (font, &x_scale, &y_scale);
-  cairo_scale (cr, x_scale, y_scale);
+  cairo_scale (cr, x_scale, -y_scale);
 
 
   cairo_glyph_t cairo_glyph = { glyph, 0, 0 };
   cairo_glyph_t cairo_glyph = { glyph, 0, 0 };
   cairo_set_scaled_font (cr, c->scaled_font);
   cairo_set_scaled_font (cr, c->scaled_font);
@@ -597,7 +597,9 @@ hb_cairo_render_glyph (cairo_scaled_font_t  *scaled_font,
 
 
   hb_position_t x_scale, y_scale;
   hb_position_t x_scale, y_scale;
   hb_font_get_scale (font, &x_scale, &y_scale);
   hb_font_get_scale (font, &x_scale, &y_scale);
-  cairo_scale (cr, +1./x_scale, -1./y_scale);
+  cairo_scale (cr,
+	       +1. / (x_scale ? x_scale : 1),
+	       -1. / (y_scale ? y_scale : 1));
 
 
   hb_font_draw_glyph (font, glyph, hb_cairo_draw_get_funcs (), cr);
   hb_font_draw_glyph (font, glyph, hb_cairo_draw_get_funcs (), cr);
 
 
@@ -628,7 +630,9 @@ hb_cairo_render_color_glyph (cairo_scaled_font_t  *scaled_font,
   hb_color_t color = HB_COLOR (0, 0, 0, 255);
   hb_color_t color = HB_COLOR (0, 0, 0, 255);
   hb_position_t x_scale, y_scale;
   hb_position_t x_scale, y_scale;
   hb_font_get_scale (font, &x_scale, &y_scale);
   hb_font_get_scale (font, &x_scale, &y_scale);
-  cairo_scale (cr, +1./x_scale, -1./y_scale);
+  cairo_scale (cr,
+	       +1. / (x_scale ? x_scale : 1),
+	       -1. / (y_scale ? y_scale : 1));
 
 
   hb_cairo_context_t c;
   hb_cairo_context_t c;
   c.scaled_font = scaled_font;
   c.scaled_font = scaled_font;
@@ -1000,6 +1004,7 @@ hb_cairo_glyphs_from_buffer (hb_buffer_t *buffer,
 	    end = start + hb_glyph[i].cluster - hb_glyph[i+1].cluster;
 	    end = start + hb_glyph[i].cluster - hb_glyph[i+1].cluster;
 	  else
 	  else
 	    end = (const char *) hb_utf_offset_to_pointer<hb_utf8_t> ((const uint8_t *) start,
 	    end = (const char *) hb_utf_offset_to_pointer<hb_utf8_t> ((const uint8_t *) start,
+								      (const uint8_t *) utf8, utf8_len,
 								      (signed) (hb_glyph[i].cluster - hb_glyph[i+1].cluster));
 								      (signed) (hb_glyph[i].cluster - hb_glyph[i+1].cluster));
 	  (*clusters)[cluster].num_bytes = end - start;
 	  (*clusters)[cluster].num_bytes = end - start;
 	  start = end;
 	  start = end;
@@ -1020,6 +1025,7 @@ hb_cairo_glyphs_from_buffer (hb_buffer_t *buffer,
 	    end = start + hb_glyph[i].cluster - hb_glyph[i-1].cluster;
 	    end = start + hb_glyph[i].cluster - hb_glyph[i-1].cluster;
 	  else
 	  else
 	    end = (const char *) hb_utf_offset_to_pointer<hb_utf8_t> ((const uint8_t *) start,
 	    end = (const char *) hb_utf_offset_to_pointer<hb_utf8_t> ((const uint8_t *) start,
+								      (const uint8_t *) utf8, utf8_len,
 								      (signed) (hb_glyph[i].cluster - hb_glyph[i-1].cluster));
 								      (signed) (hb_glyph[i].cluster - hb_glyph[i-1].cluster));
 	  (*clusters)[cluster].num_bytes = end - start;
 	  (*clusters)[cluster].num_bytes = end - start;
 	  start = end;
 	  start = end;

+ 1 - 1
thirdparty/harfbuzz/src/hb-cff-interp-common.hh

@@ -522,7 +522,7 @@ struct parsed_values_t
 
 
   void alloc (unsigned n)
   void alloc (unsigned n)
   {
   {
-    values.alloc (n, true);
+    values.alloc_exact (n);
   }
   }
 
 
   void add_op (op_code_t op, const byte_str_ref_t& str_ref = byte_str_ref_t (), const VAL &v = VAL ())
   void add_op (op_code_t op, const byte_str_ref_t& str_ref = byte_str_ref_t (), const VAL &v = VAL ())

+ 3 - 0
thirdparty/harfbuzz/src/hb-common.cc

@@ -625,6 +625,9 @@ hb_script_get_horizontal_direction (hb_script_t script)
     /* Unicode-14.0 additions */
     /* Unicode-14.0 additions */
     case HB_SCRIPT_OLD_UYGHUR:
     case HB_SCRIPT_OLD_UYGHUR:
 
 
+    /* Unicode-16.0 additions */
+    case HB_SCRIPT_GARAY:
+
       return HB_DIRECTION_RTL;
       return HB_DIRECTION_RTL;
 
 
 
 

+ 1 - 2
thirdparty/harfbuzz/src/hb-config.hh

@@ -68,8 +68,6 @@
 #define HB_NO_FACE_COLLECT_UNICODES
 #define HB_NO_FACE_COLLECT_UNICODES
 #define HB_NO_GETENV
 #define HB_NO_GETENV
 #define HB_NO_HINTING
 #define HB_NO_HINTING
-#define HB_NO_LANGUAGE_LONG
-#define HB_NO_LANGUAGE_PRIVATE_SUBTAG
 #define HB_NO_LAYOUT_FEATURE_PARAMS
 #define HB_NO_LAYOUT_FEATURE_PARAMS
 #define HB_NO_LAYOUT_COLLECT_GLYPHS
 #define HB_NO_LAYOUT_COLLECT_GLYPHS
 #define HB_NO_LAYOUT_RARELY_USED
 #define HB_NO_LAYOUT_RARELY_USED
@@ -159,6 +157,7 @@
 #define HB_NO_FALLBACK_SHAPE
 #define HB_NO_FALLBACK_SHAPE
 #define HB_NO_OT_KERN
 #define HB_NO_OT_KERN
 #define HB_NO_OT_LAYOUT_BLOCKLIST
 #define HB_NO_OT_LAYOUT_BLOCKLIST
+#define HB_NO_AAT_LAYOUT_BLOCKLIST
 #define HB_NO_OT_SHAPE_FALLBACK
 #define HB_NO_OT_SHAPE_FALLBACK
 #endif
 #endif
 
 

+ 93 - 15
thirdparty/harfbuzz/src/hb-coretext-font.cc

@@ -34,8 +34,12 @@
 #include "hb-font.hh"
 #include "hb-font.hh"
 #include "hb-machinery.hh"
 #include "hb-machinery.hh"
 
 
-#if MAC_OS_X_VERSION_MIN_REQUIRED < 101100
+#if (defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1080) \
+    || (defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) && __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ < 60000) \
+    || (defined(__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__) && __ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ < 90000)
 #  define kCTFontOrientationDefault kCTFontDefaultOrientation
 #  define kCTFontOrientationDefault kCTFontDefaultOrientation
+#  define kCTFontOrientationHorizontal kCTFontHorizontalOrientation
+#  define kCTFontOrientationVertical kCTFontVerticalOrientation
 #endif
 #endif
 
 
 #define MAX_GLYPHS 64u
 #define MAX_GLYPHS 64u
@@ -56,11 +60,25 @@ hb_coretext_get_nominal_glyph (hb_font_t *font HB_UNUSED,
 			       void *user_data HB_UNUSED)
 			       void *user_data HB_UNUSED)
 {
 {
   CTFontRef ct_font = (CTFontRef) font_data;
   CTFontRef ct_font = (CTFontRef) font_data;
-  UniChar ch = unicode;
-  CGGlyph cg_glyph;
-  if (CTFontGetGlyphsForCharacters (ct_font, &ch, &cg_glyph, 1))
+  UniChar ch[2];
+  CGGlyph cg_glyph[2];
+  unsigned count = 0;
+
+  if (unicode <= 0xFFFF)
+  {
+    ch[count++] = unicode;
+  }
+  else if (unicode <= 0x10FFFF)
   {
   {
-    *glyph = cg_glyph;
+    ch[count++] = (unicode >> 10) + 0xD7C0;
+    ch[count++] = (unicode & 0x3FF) + 0xDC00;
+  }
+  else
+    ch[count++] = 0xFFFD;
+
+  if (CTFontGetGlyphsForCharacters (ct_font, ch, cg_glyph, count))
+  {
+    *glyph = cg_glyph[0];
     return true;
     return true;
   }
   }
   return false;
   return false;
@@ -76,6 +94,31 @@ hb_coretext_get_nominal_glyphs (hb_font_t *font HB_UNUSED,
 				unsigned int glyph_stride,
 				unsigned int glyph_stride,
 				void *user_data HB_UNUSED)
 				void *user_data HB_UNUSED)
 {
 {
+  // If any non-BMP codepoint is requested, use the slow path.
+  bool slow_path = false;
+  auto *unicode = first_unicode;
+  for (unsigned i = 0; i < count; i++)
+  {
+    if (*unicode > 0xFFFF)
+    {
+      slow_path = true;
+      break;
+    }
+    unicode = &StructAtOffset<const hb_codepoint_t> (unicode, unicode_stride);
+  }
+
+  if (unlikely (slow_path))
+  {
+    for (unsigned i = 0; i < count; i++)
+    {
+      if (!hb_coretext_get_nominal_glyph (font, font_data, *first_unicode, first_glyph, nullptr))
+	return i;
+      first_unicode = &StructAtOffset<const hb_codepoint_t> (first_unicode, unicode_stride);
+      first_glyph = &StructAtOffset<hb_codepoint_t> (first_glyph, glyph_stride);
+    }
+    return count;
+  }
+
   CTFontRef ct_font = (CTFontRef) font_data;
   CTFontRef ct_font = (CTFontRef) font_data;
 
 
   UniChar ch[MAX_GLYPHS];
   UniChar ch[MAX_GLYPHS];
@@ -88,7 +131,16 @@ hb_coretext_get_nominal_glyphs (hb_font_t *font HB_UNUSED,
       ch[j] = *first_unicode;
       ch[j] = *first_unicode;
       first_unicode = &StructAtOffset<const hb_codepoint_t> (first_unicode, unicode_stride);
       first_unicode = &StructAtOffset<const hb_codepoint_t> (first_unicode, unicode_stride);
     }
     }
-    CTFontGetGlyphsForCharacters (ct_font, ch, cg_glyph, c);
+    if (unlikely (!CTFontGetGlyphsForCharacters (ct_font, ch, cg_glyph, c)))
+    {
+      // Use slow path partially and return at first failure.
+      for (unsigned j = 0; j < c; j++)
+      {
+	if (!hb_coretext_get_nominal_glyph (font, font_data, ch[j], first_glyph, nullptr))
+	  return i + j;
+	first_glyph = &StructAtOffset<hb_codepoint_t> (first_glyph, glyph_stride);
+      }
+    }
     for (unsigned j = 0; j < c; j++)
     for (unsigned j = 0; j < c; j++)
     {
     {
       *first_glyph = cg_glyph[j];
       *first_glyph = cg_glyph[j];
@@ -109,13 +161,38 @@ hb_coretext_get_variation_glyph (hb_font_t *font HB_UNUSED,
 {
 {
   CTFontRef ct_font = (CTFontRef) font_data;
   CTFontRef ct_font = (CTFontRef) font_data;
 
 
-  UniChar ch[2] = { unicode, variation_selector };
-  CGGlyph cg_glyph[2];
+  UniChar ch[4];
+  CGGlyph cg_glyph[4];
+  unsigned count = 0;
+
+  // Add Unicode, then variation selector. Ugly, but works.
+  //
+  if (unicode <= 0xFFFF)
+    ch[count++] = unicode;
+  else if (unicode <= 0x10FFFF)
+  {
+    ch[count++] = (unicode >> 10) + 0xD7C0;
+    ch[count++] = (unicode & 0x3FF) + 0xDC00;
+  }
+  else
+    ch[count++] = 0xFFFD;
 
 
-  CTFontGetGlyphsForCharacters (ct_font, ch, cg_glyph, 2);
+  if (variation_selector <= 0xFFFF)
+    ch[count++] = variation_selector;
+  else if (variation_selector <= 0x10FFFF)
+  {
+    ch[count++] = (variation_selector >> 10) + 0xD7C0;
+    ch[count++] = (variation_selector & 0x3FF) + 0xDC00;
+  }
+  else
+    ch[count++] = 0xFFFD;
 
 
-  if (cg_glyph[1])
-    return false;
+  CTFontGetGlyphsForCharacters (ct_font, ch, cg_glyph, count);
+
+  // All except for first should be zero if we succeeded
+  for (unsigned i = 1; i < count; i++)
+    if (cg_glyph[i])
+      return false;
 
 
   *glyph = cg_glyph[0];
   *glyph = cg_glyph[0];
   return true;
   return true;
@@ -434,10 +511,6 @@ _hb_coretext_get_font_funcs ()
  * created with hb_face_create(), and therefore was not
  * created with hb_face_create(), and therefore was not
  * initially configured to use CoreText font functions.
  * initially configured to use CoreText font functions.
  *
  *
- * An #hb_font_t object created with hb_coretext_font_create()
- * is preconfigured for CoreText font functions and does not
- * require this function to be used.
- *
  * <note>Note: Internally, this function creates a CTFont.
  * <note>Note: Internally, this function creates a CTFont.
 * </note>
 * </note>
  *
  *
@@ -448,7 +521,12 @@ hb_coretext_font_set_funcs (hb_font_t *font)
 {
 {
   CTFontRef ct_font = hb_coretext_font_get_ct_font (font);
   CTFontRef ct_font = hb_coretext_font_get_ct_font (font);
   if (unlikely (!ct_font))
   if (unlikely (!ct_font))
+  {
+    hb_font_set_funcs (font,
+		       hb_font_funcs_get_empty (),
+		       nullptr, nullptr);
     return;
     return;
+  }
 
 
   hb_font_set_funcs (font,
   hb_font_set_funcs (font,
 		     _hb_coretext_get_font_funcs (),
 		     _hb_coretext_get_font_funcs (),

+ 38 - 10
thirdparty/harfbuzz/src/hb-coretext-shape.cc

@@ -45,9 +45,6 @@
  * Functions for using HarfBuzz with the CoreText fonts.
  * Functions for using HarfBuzz with the CoreText fonts.
  **/
  **/
 
 
-/* https://developer.apple.com/documentation/coretext/1508745-ctfontcreatewithgraphicsfont */
-#define HB_CORETEXT_DEFAULT_FONT_SIZE 12.f
-
 static CTFontRef create_ct_font (CGFontRef cg_font, CGFloat font_size);
 static CTFontRef create_ct_font (CGFontRef cg_font, CGFloat font_size);
 
 
 static void
 static void
@@ -384,9 +381,9 @@ hb_coretext_face_create_from_file_or_fail (const char   *file_name,
 		      (CTFontDescriptorRef) CFArrayGetValueAtIndex (ct_font_desc_array, index) : nullptr;
 		      (CTFontDescriptorRef) CFArrayGetValueAtIndex (ct_font_desc_array, index) : nullptr;
   if (unlikely (!ct_font_desc))
   if (unlikely (!ct_font_desc))
   {
   {
-	  CFRelease (ct_font_desc_array);
-	  CFRelease (url);
-	  return nullptr;
+    CFRelease (ct_font_desc_array);
+    CFRelease (url);
+    return nullptr;
   }
   }
   CFRelease (url);
   CFRelease (url);
   auto ct_font = ct_font_desc ? CTFontCreateWithFontDescriptor (ct_font_desc, 0, nullptr) : nullptr;
   auto ct_font = ct_font_desc ? CTFontCreateWithFontDescriptor (ct_font_desc, 0, nullptr) : nullptr;
@@ -400,6 +397,7 @@ hb_coretext_face_create_from_file_or_fail (const char   *file_name,
     return nullptr;
     return nullptr;
 
 
   hb_face_t *face = hb_coretext_face_create (cg_font);
   hb_face_t *face = hb_coretext_face_create (cg_font);
+  CFRelease (cg_font);
   if (unlikely (hb_face_is_immutable (face)))
   if (unlikely (hb_face_is_immutable (face)))
     return nullptr;
     return nullptr;
 
 
@@ -432,7 +430,7 @@ _hb_coretext_shaper_font_data_create (hb_font_t *font)
   if (unlikely (!face_data)) return nullptr;
   if (unlikely (!face_data)) return nullptr;
   CGFontRef cg_font = (CGFontRef) (const void *) face->data.coretext;
   CGFontRef cg_font = (CGFontRef) (const void *) face->data.coretext;
 
 
-  CGFloat font_size = (CGFloat) (font->ptem <= 0.f ? HB_CORETEXT_DEFAULT_FONT_SIZE : font->ptem);
+  CGFloat font_size = (CGFloat) (font->ptem > 0.f ? font->ptem : HB_CORETEXT_DEFAULT_FONT_SIZE);
   CTFontRef ct_font = create_ct_font (cg_font, font_size);
   CTFontRef ct_font = create_ct_font (cg_font, font_size);
 
 
   if (unlikely (!ct_font))
   if (unlikely (!ct_font))
@@ -451,11 +449,11 @@ _hb_coretext_shaper_font_data_create (hb_font_t *font)
 
 
     for (unsigned i = 0; i < font->num_coords; i++)
     for (unsigned i = 0; i < font->num_coords; i++)
     {
     {
-      if (font->coords[i] == 0.) continue;
-
       hb_ot_var_axis_info_t info;
       hb_ot_var_axis_info_t info;
       unsigned int c = 1;
       unsigned int c = 1;
       hb_ot_var_get_axis_infos (font->face, i, &c, &info);
       hb_ot_var_get_axis_infos (font->face, i, &c, &info);
+      if (font->design_coords[i] == info.default_value)
+	continue;
       float v = hb_clamp (font->design_coords[i], info.min_value, info.max_value);
       float v = hb_clamp (font->design_coords[i], info.min_value, info.max_value);
 
 
       CFNumberRef tag_number = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &info.tag);
       CFNumberRef tag_number = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &info.tag);
@@ -499,7 +497,7 @@ _hb_coretext_shaper_font_data_destroy (hb_coretext_font_data_t *data)
  * CTFontRef.
  * CTFontRef.
  *
  *
  * The created font uses the default font functions implemented
  * The created font uses the default font functions implemented
- * navitely by HarfBuzz. If you want to use the CoreText font functions
+ * natively by HarfBuzz. If you want to use the CoreText font functions
  * instead (rarely needed), you can do so by calling
  * instead (rarely needed), you can do so by calling
  * by hb_coretext_font_set_funcs().
  * by hb_coretext_font_set_funcs().
  *
  *
@@ -521,6 +519,36 @@ hb_coretext_font_create (CTFontRef ct_font)
 
 
   hb_font_set_ptem (font, CTFontGetSize (ct_font));
   hb_font_set_ptem (font, CTFontGetSize (ct_font));
 
 
+  /* Copy font variations */
+  CFDictionaryRef variations = CTFontCopyVariation (ct_font);
+  if (variations)
+  {
+    hb_vector_t<hb_variation_t> vars;
+    hb_vector_t<CFTypeRef> keys;
+    hb_vector_t<CFTypeRef> values;
+
+    CFIndex count = CFDictionaryGetCount (variations);
+    if (unlikely (!vars.alloc_exact (count) || !keys.resize_exact (count) || !values.resize_exact (count)))
+      goto done;
+
+    // Fetch them one by one and collect in a vector of our own.
+    CFDictionaryGetKeysAndValues (variations, keys.arrayZ, values.arrayZ);
+    for (CFIndex i = 0; i < count; i++)
+    {
+      int tag;
+      float value;
+      CFNumberGetValue ((CFNumberRef) keys.arrayZ[i], kCFNumberIntType, &tag);
+      CFNumberGetValue ((CFNumberRef) values.arrayZ[i], kCFNumberFloatType, &value);
+
+      hb_variation_t var = {tag, value};
+      vars.push (var);
+    }
+    hb_font_set_variations (font, vars.arrayZ, vars.length);
+
+done:
+    CFRelease (variations);
+  }
+
   /* Let there be dragons here... */
   /* Let there be dragons here... */
   font->data.coretext.cmpexch (nullptr, (hb_coretext_font_data_t *) CFRetain (ct_font));
   font->data.coretext.cmpexch (nullptr, (hb_coretext_font_data_t *) CFRetain (ct_font));
 
 

+ 164 - 0
thirdparty/harfbuzz/src/hb-decycler.hh

@@ -0,0 +1,164 @@
+/*
+ * Copyright © 2025 Behdad Esfahbod
+ *
+ *  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.
+ *
+ * Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_DECYCLER_HH
+#define HB_DECYCLER_HH
+
+#include "hb.hh"
+
+/*
+ * hb_decycler_t is an efficient cycle detector for graph traversal.
+ * It's a simple tortoise-and-hare algorithm with a twist: it's
+ * designed to detect cycles while traversing a graph in a DFS manner,
+ * instead of just a linked list.
+ *
+ * For Floyd's tortoise and hare algorithm, see:
+ * https://en.wikipedia.org/wiki/Cycle_detection#Floyd's_tortoise_and_hare
+ *
+ * hb_decycler_t is O(n) in the number of nodes in the DFS traversal
+ * if there are no cycles. Unlike Floyd's algorithm, hb_decycler_t
+ * can be used in a DFS traversal, where the graph is not a simple
+ * linked list, but a tree with possible cycles.  Like Floyd's algorithm,
+ * it is constant-memory (~three  pointers).
+ *
+ * The decycler works by creating an implicit linked-list on the stack,
+ * of the path from the root to the current node, and apply Floyd's
+ * algorithm on that list as it goes.
+ *
+ * The decycler is malloc-free, and as such, much faster to use than a
+ * hb_set_t or hb_map_t equivalent.
+ *
+ * The decycler detects cycles in the graph *eventually*, not *immediately*.
+ * That is, it may not detect a cycle until the cycle is fully traversed,
+ * even multiple times. See Floyd's algorithm analysis for details.
+ *
+ * The implementation saves a pointer storage on the stack by combining
+ * this->u.decycler and this->u.next into a union.  This is possible because
+ * at any point we only need one of those values. The invariant is that
+ * after construction, and before destruction, of a node, the u.decycler
+ * field is always valid. The u.next field is only valid when the node is
+ * in the traversal path, parent to another node.
+ *
+ * There are three method's:
+ *
+ *   - hb_decycler_node_t() constructor: Creates a new node in the traversal.
+ *     The constructor takes a reference to the decycler object and inserts
+ *     itself as the latest node in the traversal path, by advancing the hare
+ *     pointer, and for every other descent, advancing the tortoise pointer.
+ *
+ *   - ~hb_decycler_node_t() destructor: Restores the decycler object to its
+ *      previous state by removing the node from the traversal path.
+ *
+ *   - bool visit(uintptr_t value): Called on every node in the graph.  Returns
+ *     true if the node is not part of a cycle, and false if it is.  The value
+ *     parameter is used to detect cycles.  It's the caller's responsibility
+ *     to ensure that the value is unique for each node in the graph.
+ *     The cycle detection is as simple as comparing the value to the value
+ *     held by the tortoise pointer, which is the Floyd's algorithm.
+ *
+ * For usage examples see test-decycler.cc.
+ */
+
+struct hb_decycler_node_t;
+
+struct hb_decycler_t
+{
+  friend struct hb_decycler_node_t;
+
+  private:
+  bool tortoise_awake = false;
+  hb_decycler_node_t *tortoise = nullptr;
+  hb_decycler_node_t *hare = nullptr;
+};
+
+struct hb_decycler_node_t
+{
+  hb_decycler_node_t (hb_decycler_t &decycler)
+  {
+    u.decycler = &decycler;
+
+    decycler.tortoise_awake = !decycler.tortoise_awake;
+
+    if (!decycler.tortoise)
+    {
+      // First node.
+      assert (decycler.tortoise_awake);
+      assert (!decycler.hare);
+      decycler.tortoise = decycler.hare = this;
+      return;
+    }
+
+    if (decycler.tortoise_awake)
+      decycler.tortoise = decycler.tortoise->u.next; // Time to move.
+
+    this->prev = decycler.hare;
+    decycler.hare->u.next = this;
+    decycler.hare = this;
+  }
+
+  ~hb_decycler_node_t ()
+  {
+    hb_decycler_t &decycler = *u.decycler;
+
+    // Inverse of the constructor.
+
+    assert (decycler.hare == this);
+    decycler.hare = prev;
+    if (prev)
+      prev->u.decycler = &decycler;
+
+    assert (decycler.tortoise);
+    if (decycler.tortoise_awake)
+      decycler.tortoise = decycler.tortoise->prev;
+
+    decycler.tortoise_awake = !decycler.tortoise_awake;
+  }
+
+  bool visit (uintptr_t value_)
+  {
+    value = value_;
+
+    hb_decycler_t &decycler = *u.decycler;
+
+    if (decycler.tortoise == this)
+      return true; // First node; not a cycle.
+
+    if (decycler.tortoise->value == value)
+      return false; // Cycle detected.
+
+    return true;
+  }
+
+  private:
+  union {
+    hb_decycler_t *decycler;
+    hb_decycler_node_t *next;
+  } u = {nullptr};
+  hb_decycler_node_t *prev = nullptr;
+  uintptr_t value = 0;
+};
+
+#endif /* HB_DECYCLER_HH */

+ 107 - 8
thirdparty/harfbuzz/src/hb-directwrite.cc

@@ -29,6 +29,7 @@
 #include "hb-shaper-impl.hh"
 #include "hb-shaper-impl.hh"
 
 
 #include <dwrite_1.h>
 #include <dwrite_1.h>
+#include <dwrite_3.h>
 
 
 #include "hb-directwrite.h"
 #include "hb-directwrite.h"
 
 
@@ -275,6 +276,8 @@ _hb_directwrite_shaper_font_data_create (hb_font_t *font)
 void
 void
 _hb_directwrite_shaper_font_data_destroy (hb_directwrite_font_data_t *data)
 _hb_directwrite_shaper_font_data_destroy (hb_directwrite_font_data_t *data)
 {
 {
+  if (data != HB_SHAPER_DATA_SUCCEEDED)
+    ((IDWriteFont *) (const void *) data)->Release();
 }
 }
 
 
 
 
@@ -839,7 +842,7 @@ _hb_directwrite_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *
 }
 }
 
 
 static void
 static void
-_hb_directwrite_font_release (void *data)
+_hb_directwrite_face_release (void *data)
 {
 {
   if (data)
   if (data)
     ((IDWriteFontFace *) data)->Release ();
     ((IDWriteFontFace *) data)->Release ();
@@ -847,7 +850,7 @@ _hb_directwrite_font_release (void *data)
 
 
 /**
 /**
  * hb_directwrite_face_create:
  * hb_directwrite_face_create:
- * @font_face: a DirectWrite IDWriteFontFace object.
+ * @dw_face: a DirectWrite IDWriteFontFace object.
  *
  *
  * Constructs a new face object from the specified DirectWrite IDWriteFontFace.
  * Constructs a new face object from the specified DirectWrite IDWriteFontFace.
  *
  *
@@ -856,14 +859,32 @@ _hb_directwrite_font_release (void *data)
  * Since: 2.4.0
  * Since: 2.4.0
  **/
  **/
 hb_face_t *
 hb_face_t *
-hb_directwrite_face_create (IDWriteFontFace *font_face)
+hb_directwrite_face_create (IDWriteFontFace *dw_face)
 {
 {
-  if (font_face)
-    font_face->AddRef ();
-  return hb_face_create_for_tables (_hb_directwrite_reference_table, font_face,
-				    _hb_directwrite_font_release);
+  if (dw_face)
+    dw_face->AddRef ();
+  return hb_face_create_for_tables (_hb_directwrite_reference_table, dw_face,
+				    _hb_directwrite_face_release);
 }
 }
 
 
+/**
+* hb_directwrite_face_get_dw_font_face:
+* @face: a #hb_face_t object
+*
+* Gets the DirectWrite IDWriteFontFace associated with @face.
+*
+* Return value: DirectWrite IDWriteFontFace object corresponding to the given input
+*
+* Since: 10.4.0
+**/
+IDWriteFontFace *
+hb_directwrite_face_get_dw_font_face (hb_face_t *face)
+{
+  return face->data.directwrite->fontFace;
+}
+
+#ifndef HB_DISABLE_DEPRECATED
+
 /**
 /**
 * hb_directwrite_face_get_font_face:
 * hb_directwrite_face_get_font_face:
 * @face: a #hb_face_t object
 * @face: a #hb_face_t object
@@ -873,12 +894,90 @@ hb_directwrite_face_create (IDWriteFontFace *font_face)
 * Return value: DirectWrite IDWriteFontFace object corresponding to the given input
 * Return value: DirectWrite IDWriteFontFace object corresponding to the given input
 *
 *
 * Since: 2.5.0
 * Since: 2.5.0
+* Deprecated: 10.4.0: Use hb_directwrite_face_get_dw_font_face() instead
 **/
 **/
 IDWriteFontFace *
 IDWriteFontFace *
 hb_directwrite_face_get_font_face (hb_face_t *face)
 hb_directwrite_face_get_font_face (hb_face_t *face)
 {
 {
-  return face->data.directwrite->fontFace;
+  return hb_directwrite_face_get_dw_font_face (face);
+}
+
+#endif
+
+/**
+ * hb_directwrite_font_create:
+ * @dw_font: a DirectWrite IDWriteFont object.
+ *
+ * Constructs a new font object from the specified DirectWrite IDWriteFont.
+ *
+ * Return value: #hb_font_t object corresponding to the given input
+ *
+ * Since: 10.3.0
+ **/
+hb_font_t *
+hb_directwrite_font_create (IDWriteFont *dw_font)
+{
+  IDWriteFontFace *dw_face = nullptr;
+  IDWriteFontFace5 *dw_face5 = nullptr;
+
+  if (FAILED (dw_font->CreateFontFace (&dw_face)))
+    return hb_font_get_empty ();
+
+  hb_face_t *face = hb_directwrite_face_create (dw_face);
+  hb_font_t *font = hb_font_create (face);
+  hb_face_destroy (face);
+
+  if (unlikely (hb_object_is_immutable (font)))
+    goto done;
+
+  /* Copy font variations */
+  if (SUCCEEDED (dw_face->QueryInterface (__uuidof (IDWriteFontFace5), (void**) &dw_face5)))
+  {
+    if (dw_face5->HasVariations ())
+    {
+      hb_vector_t<DWRITE_FONT_AXIS_VALUE> values;
+      uint32_t count = dw_face5->GetFontAxisValueCount ();
+      if (likely (values.resize_exact (count)) &&
+	  SUCCEEDED (dw_face5->GetFontAxisValues (values.arrayZ, count)))
+      {
+	hb_vector_t<hb_variation_t> vars;
+	if (likely (vars.resize_exact (count)))
+	{
+	  for (uint32_t i = 0; i < count; ++i)
+	  {
+	    hb_tag_t tag = values[i].axisTag;
+	    float value = values[i].value;
+	    vars[i] = {tag, value};
+	  }
+	  hb_font_set_variations (font, vars.arrayZ, vars.length);
+	}
+      }
+    }
+    dw_face5->Release ();
+  }
+
+  dw_font->AddRef ();
+  font->data.directwrite.cmpexch (nullptr, (hb_directwrite_font_data_t *) dw_font);
+
+done:
+  dw_face->Release ();
+  return font;
 }
 }
 
 
+/**
+* hb_directwrite_font_get_dw_font:
+* @font: a #hb_font_t object
+*
+* Gets the DirectWrite IDWriteFont associated with @font.
+*
+* Return value: DirectWrite IDWriteFont object corresponding to the given input
+*
+* Since: 10.3.0
+**/
+IDWriteFont *
+hb_directwrite_font_get_dw_font (hb_font_t *font)
+{
+  return (IDWriteFont *) (const void *) font->data.directwrite;
+}
 
 
 #endif
 #endif

+ 15 - 1
thirdparty/harfbuzz/src/hb-directwrite.h

@@ -30,11 +30,25 @@
 HB_BEGIN_DECLS
 HB_BEGIN_DECLS
 
 
 HB_EXTERN hb_face_t *
 HB_EXTERN hb_face_t *
-hb_directwrite_face_create (IDWriteFontFace *font_face);
+hb_directwrite_face_create (IDWriteFontFace *dw_face);
 
 
+HB_EXTERN IDWriteFontFace *
+hb_directwrite_face_get_dw_font_face (hb_face_t *face);
+
+HB_EXTERN hb_font_t *
+hb_directwrite_font_create (IDWriteFont *dw_font);
+
+HB_EXTERN IDWriteFont *
+hb_directwrite_font_get_dw_font (hb_font_t *font);
+
+#ifndef HB_DISABLE_DEPRECATED
+
+HB_DEPRECATED_FOR (hb_directwrite_face_get_dw_font_face)
 HB_EXTERN IDWriteFontFace *
 HB_EXTERN IDWriteFontFace *
 hb_directwrite_face_get_font_face (hb_face_t *face);
 hb_directwrite_face_get_font_face (hb_face_t *face);
 
 
+#endif
+
 HB_END_DECLS
 HB_END_DECLS
 
 
 #endif /* HB_DIRECTWRITE_H */
 #endif /* HB_DIRECTWRITE_H */

+ 44 - 5
thirdparty/harfbuzz/src/hb-face.cc

@@ -291,6 +291,7 @@ hb_face_create_or_fail (hb_blob_t    *blob,
   return face;
   return face;
 }
 }
 
 
+#ifndef HB_NO_OPEN
 /**
 /**
  * hb_face_create_from_file_or_fail:
  * hb_face_create_from_file_or_fail:
  * @file_name: A font filename
  * @file_name: A font filename
@@ -317,6 +318,7 @@ hb_face_create_from_file_or_fail (const char   *file_name,
 
 
   return face;
   return face;
 }
 }
+#endif
 
 
 /**
 /**
  * hb_face_get_empty:
  * hb_face_get_empty:
@@ -470,7 +472,8 @@ hb_face_is_immutable (const hb_face_t *face)
  * @tag: The #hb_tag_t of the table to query
  * @tag: The #hb_tag_t of the table to query
  *
  *
  * Fetches a reference to the specified table within
  * Fetches a reference to the specified table within
- * the specified face.
+ * the specified face. Returns an empty blob if referencing table data is not
+ * possible.
  *
  *
  * Return value: (transfer full): A pointer to the @tag table within @face
  * Return value: (transfer full): A pointer to the @tag table within @face
  *
  *
@@ -490,9 +493,10 @@ hb_face_reference_table (const hb_face_t *face,
  * hb_face_reference_blob:
  * hb_face_reference_blob:
  * @face: A face object
  * @face: A face object
  *
  *
- * Fetches a pointer to the binary blob that contains the
- * specified face. Returns an empty blob if referencing face data is not
- * possible.
+ * Fetches a pointer to the binary blob that contains the specified face.
+ * If referencing the face data is not possible, this function creates a blob
+ * out of individual table blobs if hb_face_get_table_tags() works with this
+ * face, otherwise it returns an empty blob.
  *
  *
  * Return value: (transfer full): A pointer to the blob for @face
  * Return value: (transfer full): A pointer to the blob for @face
  *
  *
@@ -501,7 +505,41 @@ hb_face_reference_table (const hb_face_t *face,
 hb_blob_t *
 hb_blob_t *
 hb_face_reference_blob (hb_face_t *face)
 hb_face_reference_blob (hb_face_t *face)
 {
 {
-  return face->reference_table (HB_TAG_NONE);
+  hb_blob_t *blob = face->reference_table (HB_TAG_NONE);
+
+  if (blob == hb_blob_get_empty ())
+  {
+    // If referencing the face blob is not possible (e.g. not implemented by the
+    // font functions), use face builder to create a blob out of individual
+    // table blobs.
+    unsigned total_count = hb_face_get_table_tags (face, 0, nullptr, nullptr);
+    if (total_count)
+    {
+      hb_tag_t tags[64];
+      unsigned count = ARRAY_LENGTH (tags);
+      hb_face_t* builder = hb_face_builder_create ();
+
+      for (unsigned offset = 0; offset < total_count; offset += count)
+      {
+        hb_face_get_table_tags (face, offset, &count, tags);
+	if (unlikely (!count))
+	  break; // Allocation error
+        for (unsigned i = 0; i < count; i++)
+        {
+	  if (unlikely (!tags[i]))
+	    continue;
+	  hb_blob_t *table = hb_face_reference_table (face, tags[i]);
+	  hb_face_builder_add_table (builder, tags[i], table);
+	  hb_blob_destroy (table);
+        }
+      }
+
+      blob = hb_face_reference_blob (builder);
+      hb_face_destroy (builder);
+    }
+  }
+
+  return blob;
 }
 }
 
 
 /**
 /**
@@ -643,6 +681,7 @@ hb_face_set_get_table_tags_func (hb_face_t *face,
   {
   {
     if (destroy)
     if (destroy)
       destroy (user_data);
       destroy (user_data);
+    return;
   }
   }
 
 
   if (face->get_table_tags_destroy)
   if (face->get_table_tags_destroy)

+ 7 - 2
thirdparty/harfbuzz/src/hb-face.h

@@ -73,9 +73,14 @@ hb_face_create_from_file_or_fail (const char   *file_name,
  * @tag: the tag of the table to reference
  * @tag: the tag of the table to reference
  * @user_data: User data pointer passed by the caller
  * @user_data: User data pointer passed by the caller
  *
  *
- * Callback function for hb_face_create_for_tables().
+ * Callback function for hb_face_create_for_tables(). The @tag is the tag of the
+ * table to reference, and the special tag #HB_TAG_NONE is used to reference the
+ * blob of the face itself. If referencing the face blob is not possible, it is
+ * recommended to set hb_get_table_tags_func_t on the @face to allow
+ * hb_face_reference_blob() to create a face blob out of individual table blobs.
  *
  *
- * Return value: (transfer full): A pointer to the @tag table within @face
+ * Return value: (transfer full): A pointer to the @tag table within @face or
+ * `NULL` if the table is not found or cannot be referenced.
  *
  *
  * Since: 0.9.2
  * Since: 0.9.2
  */
  */

+ 15 - 17
thirdparty/harfbuzz/src/hb-ft-colr.hh

@@ -27,6 +27,7 @@
 
 
 #include "hb.hh"
 #include "hb.hh"
 
 
+#include "hb-decycler.hh"
 #include "hb-paint-extents.hh"
 #include "hb-paint-extents.hh"
 
 
 #include FT_COLOR_H
 #include FT_COLOR_H
@@ -105,8 +106,8 @@ struct hb_ft_paint_context_t
   FT_Color *palette;
   FT_Color *palette;
   unsigned palette_index;
   unsigned palette_index;
   hb_color_t foreground;
   hb_color_t foreground;
-  hb_map_t current_glyphs;
-  hb_map_t current_layers;
+  hb_decycler_t glyphs_decycler;
+  hb_decycler_t layers_decycler;
   int depth_left = HB_MAX_NESTING_LEVEL;
   int depth_left = HB_MAX_NESTING_LEVEL;
   int edge_count = HB_MAX_GRAPH_EDGE_COUNT;
   int edge_count = HB_MAX_GRAPH_EDGE_COUNT;
 };
 };
@@ -218,22 +219,19 @@ _hb_ft_paint (hb_ft_paint_context_t *c,
     case FT_COLR_PAINTFORMAT_COLR_LAYERS:
     case FT_COLR_PAINTFORMAT_COLR_LAYERS:
     {
     {
       FT_OpaquePaint other_paint = {0};
       FT_OpaquePaint other_paint = {0};
+      hb_decycler_node_t node (c->layers_decycler);
       while (FT_Get_Paint_Layers (ft_face,
       while (FT_Get_Paint_Layers (ft_face,
 				  &paint.u.colr_layers.layer_iterator,
 				  &paint.u.colr_layers.layer_iterator,
 				  &other_paint))
 				  &other_paint))
       {
       {
-        unsigned i = paint.u.colr_layers.layer_iterator.layer;
-
-	if (unlikely (c->current_layers.has (i)))
+	// FreeType doesn't provide a way to get the layer index, so we use the pointer
+	// for cycle detection.
+	if (unlikely (!node.visit ((uintptr_t) other_paint.p)))
 	  continue;
 	  continue;
 
 
-	c->current_layers.add (i);
-
 	c->funcs->push_group (c->data);
 	c->funcs->push_group (c->data);
 	c->recurse (other_paint);
 	c->recurse (other_paint);
 	c->funcs->pop_group (c->data, HB_PAINT_COMPOSITE_MODE_SRC_OVER);
 	c->funcs->pop_group (c->data, HB_PAINT_COMPOSITE_MODE_SRC_OVER);
-
-	c->current_layers.del (i);
       }
       }
     }
     }
     break;
     break;
@@ -333,18 +331,16 @@ _hb_ft_paint (hb_ft_paint_context_t *c,
     {
     {
       hb_codepoint_t gid = paint.u.colr_glyph.glyphID;
       hb_codepoint_t gid = paint.u.colr_glyph.glyphID;
 
 
-      if (unlikely (c->current_glyphs.has (gid)))
+      hb_decycler_node_t node (c->glyphs_decycler);
+      if (unlikely (!node.visit (gid)))
 	return;
 	return;
 
 
-      c->current_glyphs.add (gid);
-
       c->funcs->push_inverse_root_transform (c->data, c->font);
       c->funcs->push_inverse_root_transform (c->data, c->font);
       c->ft_font->lock.unlock ();
       c->ft_font->lock.unlock ();
       if (c->funcs->color_glyph (c->data, gid, c->font))
       if (c->funcs->color_glyph (c->data, gid, c->font))
       {
       {
 	c->ft_font->lock.lock ();
 	c->ft_font->lock.lock ();
 	c->funcs->pop_transform (c->data);
 	c->funcs->pop_transform (c->data);
-	c->current_glyphs.del (gid);
 	return;
 	return;
       }
       }
       c->ft_font->lock.lock ();
       c->ft_font->lock.lock ();
@@ -380,8 +376,6 @@ _hb_ft_paint (hb_ft_paint_context_t *c,
 
 
         if (has_clip_box)
         if (has_clip_box)
           c->funcs->pop_clip (c->data);
           c->funcs->pop_clip (c->data);
-
-	c->current_glyphs.del (gid);
       }
       }
     }
     }
     break;
     break;
@@ -506,7 +500,8 @@ hb_ft_paint_glyph_colr (hb_font_t *font,
     hb_ft_paint_context_t c (ft_font, font,
     hb_ft_paint_context_t c (ft_font, font,
 			     paint_funcs, paint_data,
 			     paint_funcs, paint_data,
 			     palette, palette_index, foreground);
 			     palette, palette_index, foreground);
-    c.current_glyphs.add (gid);
+    hb_decycler_node_t node (c.glyphs_decycler);
+    node.visit (gid);
 
 
     bool is_bounded = true;
     bool is_bounded = true;
     FT_ClipBox clip_box;
     FT_ClipBox clip_box;
@@ -530,7 +525,8 @@ hb_ft_paint_glyph_colr (hb_font_t *font,
       hb_ft_paint_context_t ce (ft_font, font,
       hb_ft_paint_context_t ce (ft_font, font,
 			        extents_funcs, &extents_data,
 			        extents_funcs, &extents_data,
 			        palette, palette_index, foreground);
 			        palette, palette_index, foreground);
-      ce.current_glyphs.add (gid);
+      hb_decycler_node_t node2 (ce.glyphs_decycler);
+      node2.visit (gid);
       ce.funcs->push_root_transform (ce.data, font);
       ce.funcs->push_root_transform (ce.data, font);
       ce.recurse (paint);
       ce.recurse (paint);
       ce.funcs->pop_transform (ce.data);
       ce.funcs->pop_transform (ce.data);
@@ -547,7 +543,9 @@ hb_ft_paint_glyph_colr (hb_font_t *font,
     c.funcs->push_root_transform (c.data, font);
     c.funcs->push_root_transform (c.data, font);
 
 
     if (is_bounded)
     if (is_bounded)
+     {
       c.recurse (paint);
       c.recurse (paint);
+     }
 
 
     c.funcs->pop_transform (c.data);
     c.funcs->pop_transform (c.data);
     c.funcs->pop_clip (c.data);
     c.funcs->pop_clip (c.data);

+ 78 - 7
thirdparty/harfbuzz/src/hb-ft.cc

@@ -37,7 +37,11 @@
 #include "hb-draw.hh"
 #include "hb-draw.hh"
 #include "hb-font.hh"
 #include "hb-font.hh"
 #include "hb-machinery.hh"
 #include "hb-machinery.hh"
+#ifndef HB_NO_AAT
+#include "hb-aat-layout-trak-table.hh"
+#endif
 #include "hb-ot-os2-table.hh"
 #include "hb-ot-os2-table.hh"
+#include "hb-ot-stat-table.hh"
 #include "hb-ot-shaper-arabic-pua.hh"
 #include "hb-ot-shaper-arabic-pua.hh"
 #include "hb-paint.hh"
 #include "hb-paint.hh"
 
 
@@ -275,7 +279,7 @@ hb_ft_font_get_load_flags (hb_font_t *font)
 }
 }
 
 
 /**
 /**
- * hb_ft_font_get_face: (skip)
+ * hb_ft_font_get_ft_face: (skip)
  * @font: #hb_font_t to work upon
  * @font: #hb_font_t to work upon
  *
  *
  * Fetches the FT_Face associated with the specified #hb_font_t
  * Fetches the FT_Face associated with the specified #hb_font_t
@@ -286,10 +290,10 @@ hb_ft_font_get_load_flags (hb_font_t *font)
  *
  *
  * Return value: (nullable): the FT_Face found or `NULL`
  * Return value: (nullable): the FT_Face found or `NULL`
  *
  *
- * Since: 0.9.2
+ * Since: 10.4.0
  **/
  **/
 FT_Face
 FT_Face
-hb_ft_font_get_face (hb_font_t *font)
+hb_ft_font_get_ft_face (hb_font_t *font)
 {
 {
   if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy))
   if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy))
     return nullptr;
     return nullptr;
@@ -299,6 +303,31 @@ hb_ft_font_get_face (hb_font_t *font)
   return ft_font->ft_face;
   return ft_font->ft_face;
 }
 }
 
 
+#ifndef HB_DISABLE_DEPRECATED
+
+/**
+ * hb_ft_font_get_face: (skip)
+ * @font: #hb_font_t to work upon
+ *
+ * Fetches the FT_Face associated with the specified #hb_font_t
+ * font object.
+ *
+ * This function works with #hb_font_t objects created by
+ * hb_ft_font_create() or hb_ft_font_create_referenced().
+ *
+ * Return value: (nullable): the FT_Face found or `NULL`
+ *
+ * Since: 0.9.2
+ * Deprecated: 10.4.0: Use hb_ft_font_get_ft_face() instead.
+ **/
+FT_Face
+hb_ft_font_get_face (hb_font_t *font)
+{
+  return hb_ft_font_get_ft_face (font);
+}
+
+#endif
+
 /**
 /**
  * hb_ft_font_lock_face: (skip)
  * hb_ft_font_lock_face: (skip)
  * @font: #hb_font_t to work upon
  * @font: #hb_font_t to work upon
@@ -502,6 +531,26 @@ hb_ft_get_glyph_h_advances (hb_font_t* font, void* font_data,
       first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
       first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
     }
     }
   }
   }
+
+#ifndef HB_NO_AAT
+  /* According to Ned, trak is applied by default for "modern fonts", as detected by presence of STAT table. */
+#ifndef HB_NO_STYLE
+  bool apply_trak = font->face->table.STAT->has_data () && font->face->table.trak->has_data ();
+#else
+  bool apply_trak = false;
+#endif
+  if (apply_trak)
+  {
+    hb_position_t tracking = font->face->table.trak->get_h_tracking (font);
+    first_advance = orig_first_advance;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      *first_advance += tracking;
+      first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (first_glyph, glyph_stride);
+      first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
+    }
+  }
+#endif
 }
 }
 
 
 #ifndef HB_NO_VERTICAL
 #ifndef HB_NO_VERTICAL
@@ -538,7 +587,20 @@ hb_ft_get_glyph_v_advance (hb_font_t *font,
    * have a Y growing upward.  Hence the extra negation. */
    * have a Y growing upward.  Hence the extra negation. */
 
 
   hb_position_t y_strength = font->y_scale >= 0 ? font->y_strength : -font->y_strength;
   hb_position_t y_strength = font->y_scale >= 0 ? font->y_strength : -font->y_strength;
-  return ((-v + (1<<9)) >> 10) + (font->embolden_in_place ? 0 : y_strength);
+  v = ((-v + (1<<9)) >> 10) + (font->embolden_in_place ? 0 : y_strength);
+
+#ifndef HB_NO_AAT
+  /* According to Ned, trak is applied by default for "modern fonts", as detected by presence of STAT table. */
+#ifndef HB_NO_STYLE
+  bool apply_trak = font->face->table.STAT->has_data () && font->face->table.trak->has_data ();
+#else
+  bool apply_trak = false;
+#endif
+  if (apply_trak)
+    v += font->face->table.trak->get_v_tracking (font);
+#endif
+
+  return v;
 }
 }
 #endif
 #endif
 
 
@@ -931,11 +993,15 @@ hb_ft_paint_glyph (hb_font_t *font,
   hb_lock_t lock (ft_font->lock);
   hb_lock_t lock (ft_font->lock);
   FT_Face ft_face = ft_font->ft_face;
   FT_Face ft_face = ft_font->ft_face;
 
 
+  FT_Long load_flags = ft_font->load_flags | FT_LOAD_NO_BITMAP | FT_LOAD_COLOR;
+#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 21301
+  load_flags |= FT_LOAD_NO_SVG;
+#endif
+
   /* We release the lock before calling into glyph callbacks, such that
   /* We release the lock before calling into glyph callbacks, such that
    * eg. draw API can call back into the face.*/
    * eg. draw API can call back into the face.*/
 
 
-  if (unlikely (FT_Load_Glyph (ft_face, gid,
-			       ft_font->load_flags | FT_LOAD_COLOR)))
+  if (unlikely (FT_Load_Glyph (ft_face, gid, load_flags)))
     return;
     return;
 
 
   if (ft_face->glyph->format == FT_GLYPH_FORMAT_OUTLINE)
   if (ft_face->glyph->format == FT_GLYPH_FORMAT_OUTLINE)
@@ -1286,7 +1352,7 @@ hb_ft_face_create_cached (FT_Face ft_face)
  *
  *
  * If you know you have valid reasons not to use hb_ft_font_create_referenced(),
  * If you know you have valid reasons not to use hb_ft_font_create_referenced(),
  * then it is the client program's responsibility to destroy @ft_face
  * then it is the client program's responsibility to destroy @ft_face
- * after the #hb_font_t font object has been destroyed.
+ * only after the #hb_font_t font object has been destroyed.
  *
  *
  * HarfBuzz will use the @destroy callback on the #hb_font_t font object
  * HarfBuzz will use the @destroy callback on the #hb_font_t font object
  * if it is supplied when you use this function. However, even if @destroy
  * if it is supplied when you use this function. However, even if @destroy
@@ -1594,6 +1660,11 @@ _release_blob (void *arg)
 void
 void
 hb_ft_font_set_funcs (hb_font_t *font)
 hb_ft_font_set_funcs (hb_font_t *font)
 {
 {
+  // In case of failure...
+  hb_font_set_funcs (font,
+		     hb_font_funcs_get_empty (),
+		     nullptr, nullptr);
+
   hb_blob_t *blob = hb_face_reference_blob (font->face);
   hb_blob_t *blob = hb_face_reference_blob (font->face);
   unsigned int blob_length;
   unsigned int blob_length;
   const char *blob_data = hb_blob_get_data (blob, &blob_length);
   const char *blob_data = hb_blob_get_data (blob, &blob_length);

+ 8 - 1
thirdparty/harfbuzz/src/hb-ft.h

@@ -111,7 +111,7 @@ HB_EXTERN hb_font_t *
 hb_ft_font_create_referenced (FT_Face ft_face);
 hb_ft_font_create_referenced (FT_Face ft_face);
 
 
 HB_EXTERN FT_Face
 HB_EXTERN FT_Face
-hb_ft_font_get_face (hb_font_t *font);
+hb_ft_font_get_ft_face (hb_font_t *font);
 
 
 HB_EXTERN FT_Face
 HB_EXTERN FT_Face
 hb_ft_font_lock_face (hb_font_t *font);
 hb_ft_font_lock_face (hb_font_t *font);
@@ -142,6 +142,13 @@ hb_ft_hb_font_changed (hb_font_t *font);
 HB_EXTERN void
 HB_EXTERN void
 hb_ft_font_set_funcs (hb_font_t *font);
 hb_ft_font_set_funcs (hb_font_t *font);
 
 
+#ifndef HB_DISABLE_DEPRECATED
+
+HB_DEPRECATED_FOR (hb_ft_font_get_ft_face)
+HB_EXTERN FT_Face
+hb_ft_font_get_face (hb_font_t *font);
+
+#endif
 
 
 HB_END_DECLS
 HB_END_DECLS
 
 

+ 9 - 0
thirdparty/harfbuzz/src/hb-geometry.hh

@@ -83,6 +83,13 @@ struct hb_transform_t
 		  float x0, float y0) :
 		  float x0, float y0) :
     xx (xx), yx (yx), xy (xy), yy (yy), x0 (x0), y0 (y0) {}
     xx (xx), yx (yx), xy (xy), yy (yy), x0 (x0), y0 (y0) {}
 
 
+  bool is_identity () const
+  {
+    return xx == 1.f && yx == 0.f &&
+	   xy == 0.f && yy == 1.f &&
+	   x0 == 0.f && y0 == 0.f;
+  }
+
   void multiply (const hb_transform_t &o)
   void multiply (const hb_transform_t &o)
   {
   {
     /* Copied from cairo, with "o" being "a" there and "this" being "b" there. */
     /* Copied from cairo, with "o" being "a" there and "this" being "b" there. */
@@ -201,6 +208,8 @@ struct hb_transform_t
   float y0 = 0.f;
   float y0 = 0.f;
 };
 };
 
 
+#define HB_TRANSFORM_IDENTITY hb_transform_t{1.f, 0.f, 0.f, 1.f, 0.f, 0.f}
+
 struct hb_bounds_t
 struct hb_bounds_t
 {
 {
   enum status_t {
   enum status_t {

+ 1 - 1
thirdparty/harfbuzz/src/hb-null.hh

@@ -176,7 +176,7 @@ template <typename Type>
 static inline Type& Crap () {
 static inline Type& Crap () {
   static_assert (hb_null_size (Type) <= HB_NULL_POOL_SIZE, "Increase HB_NULL_POOL_SIZE.");
   static_assert (hb_null_size (Type) <= HB_NULL_POOL_SIZE, "Increase HB_NULL_POOL_SIZE.");
   Type *obj = reinterpret_cast<Type *> (_hb_CrapPool);
   Type *obj = reinterpret_cast<Type *> (_hb_CrapPool);
-  memcpy (obj, std::addressof (Null (Type)), sizeof (*obj));
+  memcpy (reinterpret_cast<void*>(obj), std::addressof (Null (Type)), sizeof (*obj));
   return *obj;
   return *obj;
 }
 }
 template <typename QType>
 template <typename QType>

+ 171 - 23
thirdparty/harfbuzz/src/hb-open-type.hh

@@ -86,21 +86,12 @@ struct IntType
 
 
     return pb->cmp (*pa);
     return pb->cmp (*pa);
   }
   }
-  template <typename Type2,
-	    hb_enable_if (std::is_integral<Type2>::value &&
-			  sizeof (Type2) < sizeof (int) &&
-			  sizeof (Type) < sizeof (int))>
-  int cmp (Type2 a) const
-  {
-    Type b = v;
-    return (int) a - (int) b;
-  }
   template <typename Type2,
   template <typename Type2,
 	    hb_enable_if (hb_is_convertible (Type2, Type))>
 	    hb_enable_if (hb_is_convertible (Type2, Type))>
   int cmp (Type2 a) const
   int cmp (Type2 a) const
   {
   {
     Type b = v;
     Type b = v;
-    return a < b ? -1 : a == b ? 0 : +1;
+    return (a > b) - (a < b);
   }
   }
   bool sanitize (hb_sanitize_context_t *c) const
   bool sanitize (hb_sanitize_context_t *c) const
   {
   {
@@ -299,11 +290,6 @@ typedef Index NameID;
 struct VarIdx : HBUINT32 {
 struct VarIdx : HBUINT32 {
   static constexpr unsigned NO_VARIATION = 0xFFFFFFFFu;
   static constexpr unsigned NO_VARIATION = 0xFFFFFFFFu;
   static_assert (NO_VARIATION == HB_OT_LAYOUT_NO_VARIATIONS_INDEX, "");
   static_assert (NO_VARIATION == HB_OT_LAYOUT_NO_VARIATIONS_INDEX, "");
-  static uint32_t add (uint32_t i, unsigned short v)
-  {
-    if (i == NO_VARIATION) return i;
-    return i + v;
-  }
   VarIdx& operator = (uint32_t i) { HBUINT32::operator= (i); return *this; }
   VarIdx& operator = (uint32_t i) { HBUINT32::operator= (i); return *this; }
 };
 };
 DECLARE_NULL_NAMESPACE_BYTES (OT, VarIdx);
 DECLARE_NULL_NAMESPACE_BYTES (OT, VarIdx);
@@ -1747,6 +1733,19 @@ struct TupleValues
       else if ((control & VALUES_SIZE_MASK) ==  VALUES_ARE_WORDS)
       else if ((control & VALUES_SIZE_MASK) ==  VALUES_ARE_WORDS)
       {
       {
         if (unlikely (p + run_count * HBINT16::static_size > end)) return false;
         if (unlikely (p + run_count * HBINT16::static_size > end)) return false;
+#ifndef HB_OPTIMIZE_SIZE
+        for (; i + 3 < stop; i += 4)
+	{
+	  values.arrayZ[i] = * (const HBINT16 *) p;
+	  p += HBINT16::static_size;
+	  values.arrayZ[i + 1] = * (const HBINT16 *) p;
+	  p += HBINT16::static_size;
+	  values.arrayZ[i + 2] = * (const HBINT16 *) p;
+	  p += HBINT16::static_size;
+	  values.arrayZ[i + 3] = * (const HBINT16 *) p;
+	  p += HBINT16::static_size;
+	}
+#endif
         for (; i < stop; i++)
         for (; i < stop; i++)
         {
         {
           values.arrayZ[i] = * (const HBINT16 *) p;
           values.arrayZ[i] = * (const HBINT16 *) p;
@@ -1765,10 +1764,17 @@ struct TupleValues
       else if ((control & VALUES_SIZE_MASK) ==  VALUES_ARE_BYTES)
       else if ((control & VALUES_SIZE_MASK) ==  VALUES_ARE_BYTES)
       {
       {
         if (unlikely (p + run_count > end)) return false;
         if (unlikely (p + run_count > end)) return false;
+#ifndef HB_OPTIMIZE_SIZE
+	for (; i + 3 < stop; i += 4)
+	{
+	  values.arrayZ[i] = * (const HBINT8 *) p++;
+	  values.arrayZ[i + 1] = * (const HBINT8 *) p++;
+	  values.arrayZ[i + 2] = * (const HBINT8 *) p++;
+	  values.arrayZ[i + 3] = * (const HBINT8 *) p++;
+	}
+#endif
         for (; i < stop; i++)
         for (; i < stop; i++)
-        {
           values.arrayZ[i] = * (const HBINT8 *) p++;
           values.arrayZ[i] = * (const HBINT8 *) p++;
-        }
       }
       }
     }
     }
     return true;
     return true;
@@ -1777,12 +1783,12 @@ struct TupleValues
   struct iter_t : hb_iter_with_fallback_t<iter_t, int>
   struct iter_t : hb_iter_with_fallback_t<iter_t, int>
   {
   {
     iter_t (const unsigned char *p_, unsigned len_)
     iter_t (const unsigned char *p_, unsigned len_)
-	    : p (p_), end (p_ + len_)
+	    : p (p_), endp (p_ + len_)
     { if (ensure_run ()) read_value (); }
     { if (ensure_run ()) read_value (); }
 
 
     private:
     private:
     const unsigned char *p;
     const unsigned char *p;
-    const unsigned char * const end;
+    const unsigned char * const endp;
     int current_value = 0;
     int current_value = 0;
     signed run_count = 0;
     signed run_count = 0;
     unsigned width = 0;
     unsigned width = 0;
@@ -1791,7 +1797,7 @@ struct TupleValues
     {
     {
       if (likely (run_count > 0)) return true;
       if (likely (run_count > 0)) return true;
 
 
-      if (unlikely (p >= end))
+      if (unlikely (p >= endp))
       {
       {
         run_count = 0;
         run_count = 0;
         current_value = 0;
         current_value = 0;
@@ -1810,7 +1816,7 @@ struct TupleValues
 	default: assert (false);
 	default: assert (false);
       }
       }
 
 
-      if (unlikely (p + run_count * width > end))
+      if (unlikely (p + run_count * width > endp))
       {
       {
 	run_count = 0;
 	run_count = 0;
 	current_value = 0;
 	current_value = 0;
@@ -1837,7 +1843,7 @@ struct TupleValues
     __item_t__ __item__ () const
     __item_t__ __item__ () const
     { return current_value; }
     { return current_value; }
 
 
-    bool __more__ () const { return run_count || p < end; }
+    bool __more__ () const { return run_count || p < endp; }
     void __next__ ()
     void __next__ ()
     {
     {
       run_count--;
       run_count--;
@@ -1864,10 +1870,146 @@ struct TupleValues
     { return p != o.p || run_count != o.run_count; }
     { return p != o.p || run_count != o.run_count; }
     iter_t __end__ () const
     iter_t __end__ () const
     {
     {
-      iter_t it (end, 0);
+      iter_t it (endp, 0);
       return it;
       return it;
     }
     }
   };
   };
+
+  struct fetcher_t
+  {
+    fetcher_t (const unsigned char *p_, unsigned len_)
+	      : p (p_), end (p_ + len_) {}
+
+    private:
+    const unsigned char *p;
+    const unsigned char * const end;
+    signed run_count = 0;
+    unsigned width = 0;
+
+    bool ensure_run ()
+    {
+      if (likely (run_count > 0)) return true;
+
+      if (unlikely (p >= end))
+      {
+        run_count = 0;
+	return false;
+      }
+
+      unsigned control = *p++;
+      run_count = (control & VALUE_RUN_COUNT_MASK) + 1;
+      width = control & VALUES_SIZE_MASK;
+      switch (width)
+      {
+        case VALUES_ARE_ZEROS: width = 0; break;
+	case VALUES_ARE_BYTES: width = HBINT8::static_size;  break;
+	case VALUES_ARE_WORDS: width = HBINT16::static_size; break;
+	case VALUES_ARE_LONGS: width = HBINT32::static_size; break;
+	default: assert (false);
+      }
+
+      if (unlikely (p + run_count * width > end))
+      {
+	run_count = 0;
+	return false;
+      }
+
+      return true;
+    }
+
+    void skip (unsigned n)
+    {
+      while (n)
+      {
+	if (unlikely (!ensure_run ()))
+	  return;
+	unsigned i = hb_min (n, (unsigned) run_count);
+	run_count -= i;
+	n -= i;
+	p += i * width;
+      }
+    }
+
+    template <bool scaled>
+    void _add_to (hb_array_t<float> out, float scale = 1.0f)
+    {
+      unsigned n = out.length;
+      float *arrayZ = out.arrayZ;
+
+      for (unsigned i = 0; i < n;)
+      {
+	if (unlikely (!ensure_run ()))
+	  break;
+	unsigned count = hb_min (n - i, (unsigned) run_count);
+	switch (width)
+	{
+	  case 1:
+	  {
+	    const auto *pp = (const HBINT8 *) p;
+	    unsigned j = 0;
+#ifndef HB_OPTIMIZE_SIZE
+	    for (; j + 3 < count; j += 4)
+	    {
+	      *arrayZ++ += scaled ? *pp++ * scale : *pp++;
+	      *arrayZ++ += scaled ? *pp++ * scale : *pp++;
+	      *arrayZ++ += scaled ? *pp++ * scale : *pp++;
+	      *arrayZ++ += scaled ? *pp++ * scale : *pp++;
+	    }
+#endif
+	    for (; j < count; j++)
+	      *arrayZ++ += scaled ? *pp++ * scale : *pp++;
+	  }
+	  break;
+	  case 2:
+	  {
+	    const auto *pp = (const HBINT16 *) p;
+	    unsigned j = 0;
+#ifndef HB_OPTIMIZE_SIZE
+	    for (; j + 3 < count; j += 4)
+	    {
+	      *arrayZ++ += scaled ? *pp++ * scale : *pp++;
+	      *arrayZ++ += scaled ? *pp++ * scale : *pp++;
+	      *arrayZ++ += scaled ? *pp++ * scale : *pp++;
+	      *arrayZ++ += scaled ? *pp++ * scale : *pp++;
+	    }
+#endif
+	    for (; j < count; j++)
+	      *arrayZ++ += scaled ? *pp++ * scale : *pp++;
+	  }
+	  break;
+	  case 4:
+	  {
+	    const auto *pp = (const HBINT32 *) p;
+	    for (unsigned j = 0; j < count; j++)
+	      *arrayZ++ += scaled ? *pp++ * scale : *pp++;
+	  }
+	  break;
+	}
+	p += count * width;
+	run_count -= count;
+	i += count;
+      }
+    }
+
+    public:
+    void add_to (hb_array_t<float> out, float scale = 1.0f)
+    {
+      unsigned n = out.length;
+
+      if (scale == 0.0f)
+      {
+        skip (n);
+	return;
+      }
+
+#ifndef HB_OPTIMIZE_SIZE
+      if (scale == 1.0f)
+        _add_to<false> (out);
+      else
+#endif
+        _add_to<true> (out, scale);
+    }
+  };
 };
 };
 
 
 struct TupleList : CFF2Index
 struct TupleList : CFF2Index
@@ -1877,6 +2019,12 @@ struct TupleList : CFF2Index
     auto bytes = CFF2Index::operator [] (i);
     auto bytes = CFF2Index::operator [] (i);
     return TupleValues::iter_t (bytes.arrayZ, bytes.length);
     return TupleValues::iter_t (bytes.arrayZ, bytes.length);
   }
   }
+
+  TupleValues::fetcher_t fetcher (unsigned i) const
+  {
+    auto bytes = CFF2Index::operator [] (i);
+    return TupleValues::fetcher_t (bytes.arrayZ, bytes.length);
+  }
 };
 };
 
 
 
 

+ 0 - 9
thirdparty/harfbuzz/src/hb-ot-cff1-table.cc

@@ -553,15 +553,6 @@ bool _get_path (const OT::cff1::accelerator_t *cff, hb_font_t *font, hb_codepoin
   return true;
   return true;
 }
 }
 
 
-bool OT::cff1::accelerator_t::paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const
-{
-  funcs->push_clip_glyph (data, glyph, font);
-  funcs->color (data, true, foreground);
-  funcs->pop_clip (data);
-
-  return true;
-}
-
 bool OT::cff1::accelerator_t::get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const
 bool OT::cff1::accelerator_t::get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const
 {
 {
 #ifdef HB_NO_OT_FONT_CFF
 #ifdef HB_NO_OT_FONT_CFF

+ 0 - 1
thirdparty/harfbuzz/src/hb-ot-cff1-table.hh

@@ -1462,7 +1462,6 @@ struct cff1
     }
     }
 
 
     HB_INTERNAL bool get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const;
     HB_INTERNAL bool get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const;
-    HB_INTERNAL bool paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const;
     HB_INTERNAL bool get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const;
     HB_INTERNAL bool get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const;
 
 
     private:
     private:

+ 0 - 9
thirdparty/harfbuzz/src/hb-ot-cff2-table.cc

@@ -143,15 +143,6 @@ bool OT::cff2::accelerator_t::get_extents (hb_font_t *font,
   return true;
   return true;
 }
 }
 
 
-bool OT::cff2::accelerator_t::paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const
-{
-  funcs->push_clip_glyph (data, glyph, font);
-  funcs->color (data, true, foreground);
-  funcs->pop_clip (data);
-
-  return true;
-}
-
 struct cff2_path_param_t
 struct cff2_path_param_t
 {
 {
   cff2_path_param_t (hb_font_t *font_, hb_draw_session_t &draw_session_)
   cff2_path_param_t (hb_font_t *font_, hb_draw_session_t &draw_session_)

+ 0 - 1
thirdparty/harfbuzz/src/hb-ot-cff2-table.hh

@@ -518,7 +518,6 @@ struct cff2
     HB_INTERNAL bool get_extents (hb_font_t *font,
     HB_INTERNAL bool get_extents (hb_font_t *font,
 				  hb_codepoint_t glyph,
 				  hb_codepoint_t glyph,
 				  hb_glyph_extents_t *extents) const;
 				  hb_glyph_extents_t *extents) const;
-    HB_INTERNAL bool paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const;
     HB_INTERNAL bool get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const;
     HB_INTERNAL bool get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const;
     HB_INTERNAL bool get_path_at (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session, hb_array_t<const int> coords) const;
     HB_INTERNAL bool get_path_at (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session, hb_array_t<const int> coords) const;
   };
   };

+ 4 - 0
thirdparty/harfbuzz/src/hb-ot-cmap-table.hh

@@ -1397,6 +1397,9 @@ struct CmapSubtableFormat14
     hb_vector_t<hb_pair_t<unsigned, unsigned>> obj_indices;
     hb_vector_t<hb_pair_t<unsigned, unsigned>> obj_indices;
     for (int i = src_tbl->record.len - 1; i >= 0; i--)
     for (int i = src_tbl->record.len - 1; i >= 0; i--)
     {
     {
+      if (!unicodes->has(src_tbl->record[i].varSelector))
+        continue;
+
       hb_pair_t<unsigned, unsigned> result = src_tbl->record[i].copy (c, unicodes, glyphs_requested, glyph_map, base);
       hb_pair_t<unsigned, unsigned> result = src_tbl->record[i].copy (c, unicodes, glyphs_requested, glyph_map, base);
       if (result.first || result.second)
       if (result.first || result.second)
 	obj_indices.push (result);
 	obj_indices.push (result);
@@ -1453,6 +1456,7 @@ struct CmapSubtableFormat14
   {
   {
     + hb_iter (record)
     + hb_iter (record)
     | hb_filter (hb_bool, &VariationSelectorRecord::nonDefaultUVS)
     | hb_filter (hb_bool, &VariationSelectorRecord::nonDefaultUVS)
+    | hb_filter (unicodes, &VariationSelectorRecord::varSelector)
     | hb_map (&VariationSelectorRecord::nonDefaultUVS)
     | hb_map (&VariationSelectorRecord::nonDefaultUVS)
     | hb_map (hb_add (this))
     | hb_map (hb_add (this))
     | hb_apply ([=] (const NonDefaultUVS& _) { _.closure_glyphs (unicodes, glyphset); })
     | hb_apply ([=] (const NonDefaultUVS& _) { _.closure_glyphs (unicodes, glyphset); })

+ 4 - 1
thirdparty/harfbuzz/src/hb-ot-face-table-list.hh

@@ -95,9 +95,12 @@ HB_OT_CORE_TABLE (OT, fvar)
 HB_OT_CORE_TABLE (OT, avar)
 HB_OT_CORE_TABLE (OT, avar)
 HB_OT_CORE_TABLE (OT, cvar)
 HB_OT_CORE_TABLE (OT, cvar)
 HB_OT_ACCELERATOR (OT, gvar)
 HB_OT_ACCELERATOR (OT, gvar)
+#ifndef HB_NO_BEYOND_64K
+HB_OT_ACCELERATOR (OT, GVAR)
+#endif
 HB_OT_CORE_TABLE (OT, MVAR)
 HB_OT_CORE_TABLE (OT, MVAR)
 #ifndef HB_NO_VAR_COMPOSITES
 #ifndef HB_NO_VAR_COMPOSITES
-HB_OT_CORE_TABLE (OT, VARC)
+HB_OT_ACCELERATOR (OT, VARC)
 #endif
 #endif
 #endif
 #endif
 
 

+ 1 - 0
thirdparty/harfbuzz/src/hb-ot-face.cc

@@ -41,6 +41,7 @@
 #include "hb-ot-layout-gdef-table.hh"
 #include "hb-ot-layout-gdef-table.hh"
 #include "hb-ot-layout-gsub-table.hh"
 #include "hb-ot-layout-gsub-table.hh"
 #include "hb-ot-layout-gpos-table.hh"
 #include "hb-ot-layout-gpos-table.hh"
+#include "hb-ot-var-varc-table.hh"
 #include "hb-aat-layout-kerx-table.hh"
 #include "hb-aat-layout-kerx-table.hh"
 #include "hb-aat-layout-morx-table.hh"
 #include "hb-aat-layout-morx-table.hh"
 
 

+ 50 - 10
thirdparty/harfbuzz/src/hb-ot-font.cc

@@ -36,13 +36,16 @@
 #include "hb-ot-face.hh"
 #include "hb-ot-face.hh"
 #include "hb-outline.hh"
 #include "hb-outline.hh"
 
 
+#ifndef HB_NO_AAT
+#include "hb-aat-layout-trak-table.hh"
+#endif
 #include "hb-ot-cmap-table.hh"
 #include "hb-ot-cmap-table.hh"
 #include "hb-ot-glyf-table.hh"
 #include "hb-ot-glyf-table.hh"
 #include "hb-ot-cff2-table.hh"
 #include "hb-ot-cff2-table.hh"
 #include "hb-ot-cff1-table.hh"
 #include "hb-ot-cff1-table.hh"
 #include "hb-ot-hmtx-table.hh"
 #include "hb-ot-hmtx-table.hh"
 #include "hb-ot-post-table.hh"
 #include "hb-ot-post-table.hh"
-#include "hb-ot-stat-table.hh" // Just so we compile it; unused otherwise.
+#include "hb-ot-stat-table.hh"
 #include "hb-ot-var-varc-table.hh"
 #include "hb-ot-var-varc-table.hh"
 #include "hb-ot-vorg-table.hh"
 #include "hb-ot-vorg-table.hh"
 #include "OT/Color/CBDT/CBDT.hh"
 #include "OT/Color/CBDT/CBDT.hh"
@@ -73,6 +76,10 @@ struct hb_ot_font_t
 {
 {
   const hb_ot_face_t *ot_face;
   const hb_ot_face_t *ot_face;
 
 
+#ifndef HB_NO_AAT
+  bool apply_trak;
+#endif
+
 #ifndef HB_NO_OT_FONT_CMAP_CACHE
 #ifndef HB_NO_OT_FONT_CMAP_CACHE
   hb_ot_font_cmap_cache_t *cmap_cache;
   hb_ot_font_cmap_cache_t *cmap_cache;
 #endif
 #endif
@@ -91,6 +98,15 @@ _hb_ot_font_create (hb_font_t *font)
 
 
   ot_font->ot_face = &font->face->table;
   ot_font->ot_face = &font->face->table;
 
 
+#ifndef HB_NO_AAT
+  /* According to Ned, trak is applied by default for "modern fonts", as detected by presence of STAT table. */
+#ifndef HB_NO_STYLE
+  ot_font->apply_trak = font->face->table.STAT->has_data () && font->face->table.trak->has_data ();
+#else
+  ot_font->apply_trak = false;
+#endif
+#endif
+
 #ifndef HB_NO_OT_FONT_CMAP_CACHE
 #ifndef HB_NO_OT_FONT_CMAP_CACHE
   // retry:
   // retry:
   auto *cmap_cache  = (hb_ot_font_cmap_cache_t *) hb_face_get_user_data (font->face,
   auto *cmap_cache  = (hb_ot_font_cmap_cache_t *) hb_face_get_user_data (font->face,
@@ -200,7 +216,6 @@ hb_ot_get_glyph_h_advances (hb_font_t* font, void* font_data,
 			    unsigned advance_stride,
 			    unsigned advance_stride,
 			    void *user_data HB_UNUSED)
 			    void *user_data HB_UNUSED)
 {
 {
-
   const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data;
   const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data;
   const hb_ot_face_t *ot_face = ot_font->ot_face;
   const hb_ot_face_t *ot_face = ot_font->ot_face;
   const OT::hmtx_accelerator_t &hmtx = *ot_face->hmtx;
   const OT::hmtx_accelerator_t &hmtx = *ot_face->hmtx;
@@ -292,6 +307,20 @@ hb_ot_get_glyph_h_advances (hb_font_t* font, void* font_data,
       first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
       first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
     }
     }
   }
   }
+
+#ifndef HB_NO_AAT
+  if (ot_font->apply_trak)
+  {
+    hb_position_t tracking = font->face->table.trak->get_h_tracking (font);
+    first_advance = orig_first_advance;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      *first_advance += tracking;
+      first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (first_glyph, glyph_stride);
+      first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
+    }
+  }
+#endif
 }
 }
 
 
 #ifndef HB_NO_VERTICAL
 #ifndef HB_NO_VERTICAL
@@ -356,6 +385,20 @@ hb_ot_get_glyph_v_advances (hb_font_t* font, void* font_data,
       first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
       first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
     }
     }
   }
   }
+
+#ifndef HB_NO_AAT
+  if (ot_font->apply_trak)
+  {
+    hb_position_t tracking = font->face->table.trak->get_v_tracking (font);
+    first_advance = orig_first_advance;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      *first_advance += tracking;
+      first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (first_glyph, glyph_stride);
+      first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
+    }
+  }
+#endif
 }
 }
 #endif
 #endif
 
 
@@ -568,14 +611,11 @@ hb_ot_paint_glyph (hb_font_t *font,
   if (font->face->table.sbix->paint_glyph (font, glyph, paint_funcs, paint_data)) return;
   if (font->face->table.sbix->paint_glyph (font, glyph, paint_funcs, paint_data)) return;
 #endif
 #endif
 #endif
 #endif
-#ifndef HB_NO_VAR_COMPOSITES
-  if (font->face->table.VARC->paint_glyph (font, glyph, paint_funcs, paint_data, foreground)) return;
-#endif
-  if (font->face->table.glyf->paint_glyph (font, glyph, paint_funcs, paint_data, foreground)) return;
-#ifndef HB_NO_CFF
-  if (font->face->table.cff2->paint_glyph (font, glyph, paint_funcs, paint_data, foreground)) return;
-  if (font->face->table.cff1->paint_glyph (font, glyph, paint_funcs, paint_data, foreground)) return;
-#endif
+
+  // Outline glyph
+  paint_funcs->push_clip_glyph (paint_data, glyph, font);
+  paint_funcs->color (paint_data, true, foreground);
+  paint_funcs->pop_clip (paint_data);
 }
 }
 #endif
 #endif
 
 

+ 1 - 1
thirdparty/harfbuzz/src/hb-ot-hdmx-table.hh

@@ -95,7 +95,7 @@ struct hdmx
   bool serialize (hb_serialize_context_t *c,
   bool serialize (hb_serialize_context_t *c,
 		  unsigned version,
 		  unsigned version,
 		  Iterator it,
 		  Iterator it,
-		  const hb_vector_t<hb_codepoint_pair_t> &new_to_old_gid_list,
+		  hb_array_t<const hb_codepoint_pair_t> new_to_old_gid_list,
 		  unsigned num_glyphs)
 		  unsigned num_glyphs)
   {
   {
     TRACE_SERIALIZE (this);
     TRACE_SERIALIZE (this);

+ 1 - 1
thirdparty/harfbuzz/src/hb-ot-hmtx-table.hh

@@ -182,7 +182,7 @@ struct hmtxvmtx
 	   hb_requires (hb_is_iterator (Iterator))>
 	   hb_requires (hb_is_iterator (Iterator))>
   void serialize (hb_serialize_context_t *c,
   void serialize (hb_serialize_context_t *c,
 		  Iterator it,
 		  Iterator it,
-		  const hb_vector_t<hb_codepoint_pair_t> new_to_old_gid_list,
+		  hb_array_t<const hb_codepoint_pair_t> new_to_old_gid_list,
 		  unsigned num_long_metrics,
 		  unsigned num_long_metrics,
                   unsigned total_num_metrics)
                   unsigned total_num_metrics)
   {
   {

+ 2 - 2
thirdparty/harfbuzz/src/hb-ot-kern-table.hh

@@ -342,7 +342,7 @@ struct kern
   }
   }
 
 
   bool apply (AAT::hb_aat_apply_context_t *c,
   bool apply (AAT::hb_aat_apply_context_t *c,
-	      const AAT::kern_accelerator_data_t *accel_data = nullptr) const
+	      const AAT::kern_accelerator_data_t &accel_data) const
   { return dispatch (c, accel_data); }
   { return dispatch (c, accel_data); }
 
 
   template <typename context_t, typename ...Ts>
   template <typename context_t, typename ...Ts>
@@ -395,7 +395,7 @@ struct kern
 
 
     bool apply (AAT::hb_aat_apply_context_t *c) const
     bool apply (AAT::hb_aat_apply_context_t *c) const
     {
     {
-      return table->apply (c, &accel_data);
+      return table->apply (c, accel_data);
     }
     }
 
 
     hb_blob_ptr_t<kern> table;
     hb_blob_ptr_t<kern> table;

+ 1 - 1
thirdparty/harfbuzz/src/hb-ot-layout-base-table.hh

@@ -460,7 +460,7 @@ struct BaseScript
   { return (this+baseValues).get_base_coord (baseline_tag_index); }
   { return (this+baseValues).get_base_coord (baseline_tag_index); }
 
 
   bool has_values () const { return baseValues; }
   bool has_values () const { return baseValues; }
-  bool has_min_max () const { return defaultMinMax; /* TODO What if only per-language is present? */ }
+  bool has_min_max () const { return defaultMinMax || baseLangSysRecords; }
 
 
   void collect_variation_indices (const hb_subset_plan_t* plan,
   void collect_variation_indices (const hb_subset_plan_t* plan,
                                   hb_set_t& varidx_set /* OUT */) const
                                   hb_set_t& varidx_set /* OUT */) const

+ 38 - 25
thirdparty/harfbuzz/src/hb-ot-layout-common.hh

@@ -34,6 +34,7 @@
 #include "hb-open-type.hh"
 #include "hb-open-type.hh"
 #include "hb-set.hh"
 #include "hb-set.hh"
 #include "hb-bimap.hh"
 #include "hb-bimap.hh"
+#include "hb-cache.hh"
 
 
 #include "OT/Layout/Common/Coverage.hh"
 #include "OT/Layout/Common/Coverage.hh"
 #include "OT/Layout/types.hh"
 #include "OT/Layout/types.hh"
@@ -2076,6 +2077,15 @@ struct ClassDef
     default:return 0;
     default:return 0;
     }
     }
   }
   }
+  unsigned int get_class (hb_codepoint_t glyph_id,
+			  hb_ot_lookup_cache_t *cache) const
+  {
+    unsigned klass;
+    if (cache && cache->get (glyph_id, &klass)) return klass;
+    klass = get_class (glyph_id);
+    if (cache) cache->set (glyph_id, klass);
+    return klass;
+  }
 
 
   unsigned get_population () const
   unsigned get_population () const
   {
   {
@@ -3137,23 +3147,14 @@ struct MultiVarData
   {
   {
     auto &deltaSets = StructAfter<decltype (deltaSetsX)> (regionIndices);
     auto &deltaSets = StructAfter<decltype (deltaSetsX)> (regionIndices);
 
 
-    auto values_iter = deltaSets[inner];
-
+    auto values_iter = deltaSets.fetcher (inner);
     unsigned regionCount = regionIndices.len;
     unsigned regionCount = regionIndices.len;
-    unsigned count = out.length;
     for (unsigned regionIndex = 0; regionIndex < regionCount; regionIndex++)
     for (unsigned regionIndex = 0; regionIndex < regionCount; regionIndex++)
     {
     {
       float scalar = regions.evaluate (regionIndices.arrayZ[regionIndex],
       float scalar = regions.evaluate (regionIndices.arrayZ[regionIndex],
 				       coords, coord_count,
 				       coords, coord_count,
 				       cache);
 				       cache);
-      if (scalar == 1.f)
-	for (unsigned i = 0; i < count; i++)
-	  out.arrayZ[i] += *values_iter++;
-      else if (scalar)
-	for (unsigned i = 0; i < count; i++)
-	  out.arrayZ[i] += *values_iter++ * scalar;
-      else
-        values_iter += count;
+      values_iter.add_to (out, scalar);
     }
     }
   }
   }
 
 
@@ -3439,7 +3440,7 @@ struct MultiItemVariationStore
 {
 {
   using cache_t = SparseVarRegionList::cache_t;
   using cache_t = SparseVarRegionList::cache_t;
 
 
-  cache_t *create_cache () const
+  cache_t *create_cache (hb_array_t<float> static_cache = hb_array_t<float> ()) const
   {
   {
 #ifdef HB_NO_VAR
 #ifdef HB_NO_VAR
     return nullptr;
     return nullptr;
@@ -3447,8 +3448,14 @@ struct MultiItemVariationStore
     auto &r = this+regions;
     auto &r = this+regions;
     unsigned count = r.regions.len;
     unsigned count = r.regions.len;
 
 
-    float *cache = (float *) hb_malloc (sizeof (float) * count);
-    if (unlikely (!cache)) return nullptr;
+    float *cache;
+    if (count <= static_cache.length)
+      cache = static_cache.arrayZ;
+    else
+    {
+      cache = (float *) hb_malloc (sizeof (float) * count);
+      if (unlikely (!cache)) return nullptr;
+    }
 
 
     for (unsigned i = 0; i < count; i++)
     for (unsigned i = 0; i < count; i++)
       cache[i] = REGION_CACHE_ITEM_CACHE_INVALID;
       cache[i] = REGION_CACHE_ITEM_CACHE_INVALID;
@@ -3456,7 +3463,12 @@ struct MultiItemVariationStore
     return cache;
     return cache;
   }
   }
 
 
-  static void destroy_cache (cache_t *cache) { hb_free (cache); }
+  static void destroy_cache (cache_t *cache,
+			     hb_array_t<float> static_cache = hb_array_t<float> ())
+  {
+    if (cache != static_cache.arrayZ)
+      hb_free (cache);
+  }
 
 
   private:
   private:
   void get_delta (unsigned int outer, unsigned int inner,
   void get_delta (unsigned int outer, unsigned int inner,
@@ -3731,11 +3743,13 @@ struct ItemVarStoreInstancer
 
 
   float operator() (uint32_t varIdx, unsigned short offset = 0) const
   float operator() (uint32_t varIdx, unsigned short offset = 0) const
   {
   {
+   if (!coords || varIdx == VarIdx::NO_VARIATION)
+     return 0.f;
+
+    varIdx += offset;
     if (varIdxMap)
     if (varIdxMap)
-      varIdx = varIdxMap->map (VarIdx::add (varIdx, offset));
-    else
-      varIdx += offset;
-    return coords ? varStore->get_delta (varIdx, coords, cache) : 0.f;
+      varIdx = varIdxMap->map (varIdx);
+    return varStore->get_delta (varIdx, coords, cache);
   }
   }
 
 
   const ItemVariationStore *varStore;
   const ItemVariationStore *varStore;
@@ -3767,12 +3781,11 @@ struct MultiItemVarStoreInstancer
 
 
   void operator() (hb_array_t<float> out, uint32_t varIdx, unsigned short offset = 0) const
   void operator() (hb_array_t<float> out, uint32_t varIdx, unsigned short offset = 0) const
   {
   {
-    if (coords)
+    if (coords && varIdx != VarIdx::NO_VARIATION)
     {
     {
+      varIdx += offset;
       if (varIdxMap)
       if (varIdxMap)
-	varIdx = varIdxMap->map (VarIdx::add (varIdx, offset));
-      else
-	varIdx += offset;
+	varIdx = varIdxMap->map (varIdx);
       varStore->get_delta (varIdx, coords, out, cache);
       varStore->get_delta (varIdx, coords, out, cache);
     }
     }
     else
     else
@@ -3890,8 +3903,8 @@ struct ConditionAxisRange
     {
     {
       // add axisIndex->value into the hashmap so we can check if the record is
       // add axisIndex->value into the hashmap so we can check if the record is
       // unique with variations
       // unique with variations
-      int16_t int_filter_max_val = filterRangeMaxValue.to_int ();
-      int16_t int_filter_min_val = filterRangeMinValue.to_int ();
+      uint16_t int_filter_max_val = (uint16_t) filterRangeMaxValue.to_int ();
+      uint16_t int_filter_min_val = (uint16_t) filterRangeMinValue.to_int ();
       hb_codepoint_t val = (int_filter_max_val << 16) + int_filter_min_val;
       hb_codepoint_t val = (int_filter_max_val << 16) + int_filter_min_val;
 
 
       condition_map->set (axisIndex, val);
       condition_map->set (axisIndex, val);

+ 121 - 52
thirdparty/harfbuzz/src/hb-ot-layout-gsubgpos.hh

@@ -713,6 +713,7 @@ struct hb_ot_apply_context_t :
   recurse_func_t recurse_func = nullptr;
   recurse_func_t recurse_func = nullptr;
   const GDEF &gdef;
   const GDEF &gdef;
   const GDEF::accelerator_t &gdef_accel;
   const GDEF::accelerator_t &gdef_accel;
+  const hb_ot_layout_lookup_accelerator_t *lookup_accel = nullptr;
   const ItemVariationStore &var_store;
   const ItemVariationStore &var_store;
   ItemVariationStore::cache_t *var_store_cache;
   ItemVariationStore::cache_t *var_store_cache;
   hb_set_digest_t digest;
   hb_set_digest_t digest;
@@ -762,10 +763,12 @@ struct hb_ot_apply_context_t :
 					 nullptr
 					 nullptr
 #endif
 #endif
 					),
 					),
-			digest (buffer_->digest ()),
 			direction (buffer_->props.direction),
 			direction (buffer_->props.direction),
 			has_glyph_classes (gdef.has_glyph_classes ())
 			has_glyph_classes (gdef.has_glyph_classes ())
-  { init_iters (); }
+  {
+    init_iters ();
+    buffer->collect_codepoints (digest);
+  }
 
 
   ~hb_ot_apply_context_t ()
   ~hb_ot_apply_context_t ()
   {
   {
@@ -899,6 +902,13 @@ struct hb_ot_apply_context_t :
   }
   }
 };
 };
 
 
+enum class hb_ot_lookup_cache_op_t
+{
+  CREATE,
+  ENTER,
+  LEAVE,
+  DESTROY,
+};
 
 
 struct hb_accelerate_subtables_context_t :
 struct hb_accelerate_subtables_context_t :
        hb_dispatch_context_t<hb_accelerate_subtables_context_t>
        hb_dispatch_context_t<hb_accelerate_subtables_context_t>
@@ -923,19 +933,23 @@ struct hb_accelerate_subtables_context_t :
   }
   }
 
 
   template <typename T>
   template <typename T>
-  static inline auto cache_func_ (const T *obj, hb_ot_apply_context_t *c, bool enter, hb_priority<1>) HB_RETURN (bool, obj->cache_func (c, enter) )
-  template <typename T>
-  static inline bool cache_func_ (const T *obj, hb_ot_apply_context_t *c, bool enter, hb_priority<0>) { return false; }
+  static inline auto cache_func_ (void *p,
+				  hb_ot_lookup_cache_op_t op,
+				  hb_priority<1>) HB_RETURN (void *, T::cache_func (p, op) )
+  template <typename T=void>
+  static inline void * cache_func_ (void *p,
+				    hb_ot_lookup_cache_op_t op HB_UNUSED,
+				    hb_priority<0>) { return (void *) false; }
   template <typename Type>
   template <typename Type>
-  static inline bool cache_func_to (const void *obj, hb_ot_apply_context_t *c, bool enter)
+  static inline void * cache_func_to (void *p,
+				      hb_ot_lookup_cache_op_t op)
   {
   {
-    const Type *typed_obj = (const Type *) obj;
-    return cache_func_ (typed_obj, c, enter, hb_prioritize);
+    return cache_func_<Type> (p, op, hb_prioritize);
   }
   }
 #endif
 #endif
 
 
   typedef bool (*hb_apply_func_t) (const void *obj, hb_ot_apply_context_t *c);
   typedef bool (*hb_apply_func_t) (const void *obj, hb_ot_apply_context_t *c);
-  typedef bool (*hb_cache_func_t) (const void *obj, hb_ot_apply_context_t *c, bool enter);
+  typedef void * (*hb_cache_func_t) (void *p, hb_ot_lookup_cache_op_t op);
 
 
   struct hb_applicable_t
   struct hb_applicable_t
   {
   {
@@ -972,11 +986,11 @@ struct hb_accelerate_subtables_context_t :
     }
     }
     bool cache_enter (hb_ot_apply_context_t *c) const
     bool cache_enter (hb_ot_apply_context_t *c) const
     {
     {
-      return cache_func (obj, c, true);
+      return (bool) cache_func (c, hb_ot_lookup_cache_op_t::ENTER);
     }
     }
     void cache_leave (hb_ot_apply_context_t *c) const
     void cache_leave (hb_ot_apply_context_t *c) const
     {
     {
-      cache_func (obj, c, false);
+      cache_func (c, hb_ot_lookup_cache_op_t::LEAVE);
     }
     }
 #endif
 #endif
 
 
@@ -1462,6 +1476,7 @@ static inline bool ligate_input (hb_ot_apply_context_t *c,
 	unsigned int this_comp = _hb_glyph_info_get_lig_comp (&buffer->cur());
 	unsigned int this_comp = _hb_glyph_info_get_lig_comp (&buffer->cur());
 	if (this_comp == 0)
 	if (this_comp == 0)
 	  this_comp = last_num_components;
 	  this_comp = last_num_components;
+	assert (components_so_far >= last_num_components);
 	unsigned int new_lig_comp = components_so_far - last_num_components +
 	unsigned int new_lig_comp = components_so_far - last_num_components +
 				    hb_min (this_comp, last_num_components);
 				    hb_min (this_comp, last_num_components);
 	  _hb_glyph_info_set_lig_props_for_mark (&buffer->cur(), lig_id, new_lig_comp);
 	  _hb_glyph_info_set_lig_props_for_mark (&buffer->cur(), lig_id, new_lig_comp);
@@ -1487,6 +1502,7 @@ static inline bool ligate_input (hb_ot_apply_context_t *c,
       unsigned this_comp = _hb_glyph_info_get_lig_comp (&buffer->info[i]);
       unsigned this_comp = _hb_glyph_info_get_lig_comp (&buffer->info[i]);
       if (!this_comp) break;
       if (!this_comp) break;
 
 
+      assert (components_so_far >= last_num_components);
       unsigned new_lig_comp = components_so_far - last_num_components +
       unsigned new_lig_comp = components_so_far - last_num_components +
 			      hb_min (this_comp, last_num_components);
 			      hb_min (this_comp, last_num_components);
       _hb_glyph_info_set_lig_props_for_mark (&buffer->info[i], lig_id, new_lig_comp);
       _hb_glyph_info_set_lig_props_for_mark (&buffer->info[i], lig_id, new_lig_comp);
@@ -1542,6 +1558,7 @@ static bool match_lookahead (hb_ot_apply_context_t *c,
   TRACE_APPLY (nullptr);
   TRACE_APPLY (nullptr);
 
 
   hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_context;
   hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_context;
+  assert (start_index >= 1);
   skippy_iter.reset (start_index - 1);
   skippy_iter.reset (start_index - 1);
   skippy_iter.set_match_func (match_func, match_data);
   skippy_iter.set_match_func (match_func, match_data);
   skippy_iter.set_glyph_data (lookahead);
   skippy_iter.set_glyph_data (lookahead);
@@ -1852,6 +1869,7 @@ static inline void apply_lookup (hb_ot_apply_context_t *c,
   if (match_positions != match_positions_input)
   if (match_positions != match_positions_input)
     hb_free (match_positions);
     hb_free (match_positions);
 
 
+  assert (end >= 0);
   (void) buffer->move_to (end);
   (void) buffer->move_to (end);
 }
 }
 
 
@@ -2619,25 +2637,35 @@ struct ContextFormat2_5
     unsigned c = (this+classDef).cost () * ruleSet.len;
     unsigned c = (this+classDef).cost () * ruleSet.len;
     return c >= 4 ? c : 0;
     return c >= 4 ? c : 0;
   }
   }
-  bool cache_func (hb_ot_apply_context_t *c, bool enter) const
+  static void * cache_func (void *p, hb_ot_lookup_cache_op_t op)
   {
   {
-    if (enter)
+    switch (op)
     {
     {
-      if (!HB_BUFFER_TRY_ALLOCATE_VAR (c->buffer, syllable))
-	return false;
-      auto &info = c->buffer->info;
-      unsigned count = c->buffer->len;
-      for (unsigned i = 0; i < count; i++)
-	info[i].syllable() = 255;
-      c->new_syllables = 255;
-      return true;
-    }
-    else
-    {
-      c->new_syllables = (unsigned) -1;
-      HB_BUFFER_DEALLOCATE_VAR (c->buffer, syllable);
-      return true;
+      case hb_ot_lookup_cache_op_t::CREATE:
+	return (void *) true;
+      case hb_ot_lookup_cache_op_t::ENTER:
+      {
+	hb_ot_apply_context_t *c = (hb_ot_apply_context_t *) p;
+	if (!HB_BUFFER_TRY_ALLOCATE_VAR (c->buffer, syllable))
+	  return (void *) false;
+	auto &info = c->buffer->info;
+	unsigned count = c->buffer->len;
+	for (unsigned i = 0; i < count; i++)
+	  info[i].syllable() = 255;
+	c->new_syllables = 255;
+	return (void *) true;
+      }
+      case hb_ot_lookup_cache_op_t::LEAVE:
+      {
+	hb_ot_apply_context_t *c = (hb_ot_apply_context_t *) p;
+	c->new_syllables = (unsigned) -1;
+	HB_BUFFER_DEALLOCATE_VAR (c->buffer, syllable);
+	return nullptr;
+      }
+      case hb_ot_lookup_cache_op_t::DESTROY:
+        return nullptr;
     }
     }
+    return nullptr;
   }
   }
 
 
   bool apply_cached (hb_ot_apply_context_t *c) const { return _apply (c, true); }
   bool apply_cached (hb_ot_apply_context_t *c) const { return _apply (c, true); }
@@ -2646,7 +2674,7 @@ struct ContextFormat2_5
   {
   {
     TRACE_APPLY (this);
     TRACE_APPLY (this);
     unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
     unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
-    if (likely (index == NOT_COVERED)) return_trace (false);
+    if (index == NOT_COVERED) return_trace (false);
 
 
     const ClassDef &class_def = this+classDef;
     const ClassDef &class_def = this+classDef;
 
 
@@ -2832,7 +2860,7 @@ struct ContextFormat3
   {
   {
     TRACE_APPLY (this);
     TRACE_APPLY (this);
     unsigned int index = (this+coverageZ[0]).get_coverage (c->buffer->cur().codepoint);
     unsigned int index = (this+coverageZ[0]).get_coverage (c->buffer->cur().codepoint);
-    if (likely (index == NOT_COVERED)) return_trace (false);
+    if (index == NOT_COVERED) return_trace (false);
 
 
     const LookupRecord *lookupRecord = &StructAfter<LookupRecord> (coverageZ.as_array (glyphCount));
     const LookupRecord *lookupRecord = &StructAfter<LookupRecord> (coverageZ.as_array (glyphCount));
     struct ContextApplyLookupContext lookup_context = {
     struct ContextApplyLookupContext lookup_context = {
@@ -3646,7 +3674,7 @@ struct ChainContextFormat1_4
   {
   {
     TRACE_APPLY (this);
     TRACE_APPLY (this);
     unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
     unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
-    if (likely (index == NOT_COVERED)) return_trace (false);
+    if (index == NOT_COVERED) return_trace (false);
 
 
     const ChainRuleSet &rule_set = this+ruleSet[index];
     const ChainRuleSet &rule_set = this+ruleSet[index];
     struct ChainContextApplyLookupContext lookup_context = {
     struct ChainContextApplyLookupContext lookup_context = {
@@ -3857,28 +3885,37 @@ struct ChainContextFormat2_5
 
 
   unsigned cache_cost () const
   unsigned cache_cost () const
   {
   {
-    unsigned c = (this+lookaheadClassDef).cost () * ruleSet.len;
-    return c >= 4 ? c : 0;
+    return (this+lookaheadClassDef).cost () * ruleSet.len;
   }
   }
-  bool cache_func (hb_ot_apply_context_t *c, bool enter) const
+  static void * cache_func (void *p, hb_ot_lookup_cache_op_t op)
   {
   {
-    if (enter)
+    switch (op)
     {
     {
-      if (!HB_BUFFER_TRY_ALLOCATE_VAR (c->buffer, syllable))
-	return false;
-      auto &info = c->buffer->info;
-      unsigned count = c->buffer->len;
-      for (unsigned i = 0; i < count; i++)
-	info[i].syllable() = 255;
-      c->new_syllables = 255;
-      return true;
-    }
-    else
-    {
-      c->new_syllables = (unsigned) -1;
-      HB_BUFFER_DEALLOCATE_VAR (c->buffer, syllable);
-      return true;
+      case hb_ot_lookup_cache_op_t::CREATE:
+	return (void *) true;
+      case hb_ot_lookup_cache_op_t::ENTER:
+      {
+	hb_ot_apply_context_t *c = (hb_ot_apply_context_t *) p;
+	if (!HB_BUFFER_TRY_ALLOCATE_VAR (c->buffer, syllable))
+	  return (void *) false;
+	auto &info = c->buffer->info;
+	unsigned count = c->buffer->len;
+	for (unsigned i = 0; i < count; i++)
+	  info[i].syllable() = 255;
+	c->new_syllables = 255;
+	return (void *) true;
+      }
+      case hb_ot_lookup_cache_op_t::LEAVE:
+      {
+	hb_ot_apply_context_t *c = (hb_ot_apply_context_t *) p;
+	c->new_syllables = (unsigned) -1;
+	HB_BUFFER_DEALLOCATE_VAR (c->buffer, syllable);
+	return nullptr;
+      }
+      case hb_ot_lookup_cache_op_t::DESTROY:
+        return nullptr;
     }
     }
+    return nullptr;
   }
   }
 
 
   bool apply_cached (hb_ot_apply_context_t *c) const { return _apply (c, true); }
   bool apply_cached (hb_ot_apply_context_t *c) const { return _apply (c, true); }
@@ -3887,7 +3924,7 @@ struct ChainContextFormat2_5
   {
   {
     TRACE_APPLY (this);
     TRACE_APPLY (this);
     unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
     unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
-    if (likely (index == NOT_COVERED)) return_trace (false);
+    if (index == NOT_COVERED) return_trace (false);
 
 
     const ClassDef &backtrack_class_def = this+backtrackClassDef;
     const ClassDef &backtrack_class_def = this+backtrackClassDef;
     const ClassDef &input_class_def = this+inputClassDef;
     const ClassDef &input_class_def = this+inputClassDef;
@@ -4133,7 +4170,7 @@ struct ChainContextFormat3
     const auto &input = StructAfter<decltype (inputX)> (backtrack);
     const auto &input = StructAfter<decltype (inputX)> (backtrack);
 
 
     unsigned int index = (this+input[0]).get_coverage (c->buffer->cur().codepoint);
     unsigned int index = (this+input[0]).get_coverage (c->buffer->cur().codepoint);
-    if (likely (index == NOT_COVERED)) return_trace (false);
+    if (index == NOT_COVERED) return_trace (false);
 
 
     const auto &lookahead = StructAfter<decltype (lookaheadX)> (input);
     const auto &lookahead = StructAfter<decltype (lookaheadX)> (input);
     const auto &lookup = StructAfter<decltype (lookupX)> (lookahead);
     const auto &lookup = StructAfter<decltype (lookupX)> (lookahead);
@@ -4404,7 +4441,18 @@ struct hb_ot_layout_lookup_accelerator_t
       thiz->digest.union_ (subtable.digest);
       thiz->digest.union_ (subtable.digest);
 
 
 #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
 #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
+    if (c_accelerate_subtables.cache_user_cost < 4)
+      c_accelerate_subtables.cache_user_idx = (unsigned) -1;
+
     thiz->cache_user_idx = c_accelerate_subtables.cache_user_idx;
     thiz->cache_user_idx = c_accelerate_subtables.cache_user_idx;
+
+    if (thiz->cache_user_idx != (unsigned) -1)
+    {
+      thiz->cache = thiz->subtables[thiz->cache_user_idx].cache_func (nullptr, hb_ot_lookup_cache_op_t::CREATE);
+      if (!thiz->cache)
+	thiz->cache_user_idx = (unsigned) -1;
+    }
+
     for (unsigned i = 0; i < count; i++)
     for (unsigned i = 0; i < count; i++)
       if (i != thiz->cache_user_idx)
       if (i != thiz->cache_user_idx)
 	thiz->subtables[i].apply_cached_func = thiz->subtables[i].apply_func;
 	thiz->subtables[i].apply_cached_func = thiz->subtables[i].apply_func;
@@ -4413,6 +4461,17 @@ struct hb_ot_layout_lookup_accelerator_t
     return thiz;
     return thiz;
   }
   }
 
 
+  void fini ()
+  {
+#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
+    if (cache)
+    {
+      assert (cache_user_idx != (unsigned) -1);
+      subtables[cache_user_idx].cache_func (cache, hb_ot_lookup_cache_op_t::DESTROY);
+    }
+#endif
+  }
+
   bool may_have (hb_codepoint_t g) const
   bool may_have (hb_codepoint_t g) const
   { return digest.may_have (g); }
   { return digest.may_have (g); }
 
 
@@ -4421,6 +4480,7 @@ struct hb_ot_layout_lookup_accelerator_t
 #endif
 #endif
   bool apply (hb_ot_apply_context_t *c, unsigned subtables_count, bool use_cache) const
   bool apply (hb_ot_apply_context_t *c, unsigned subtables_count, bool use_cache) const
   {
   {
+    c->lookup_accel = this;
 #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
 #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
     if (use_cache)
     if (use_cache)
     {
     {
@@ -4460,10 +4520,13 @@ struct hb_ot_layout_lookup_accelerator_t
 
 
 
 
   hb_set_digest_t digest;
   hb_set_digest_t digest;
-  private:
 #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
 #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
+  public:
+  void *cache = nullptr;
+  private:
   unsigned cache_user_idx = (unsigned) -1;
   unsigned cache_user_idx = (unsigned) -1;
 #endif
 #endif
+  private:
   hb_accelerate_subtables_context_t::hb_applicable_t subtables[HB_VAR_ARRAY];
   hb_accelerate_subtables_context_t::hb_applicable_t subtables[HB_VAR_ARRAY];
 };
 };
 
 
@@ -4848,7 +4911,12 @@ struct GSUBGPOS
     ~accelerator_t ()
     ~accelerator_t ()
     {
     {
       for (unsigned int i = 0; i < this->lookup_count; i++)
       for (unsigned int i = 0; i < this->lookup_count; i++)
-	hb_free (this->accels[i]);
+      {
+	auto *accel = this->accels[i].get_relaxed ();
+	if (accel)
+	  accel->fini ();
+	hb_free (accel);
+      }
       hb_free (this->accels);
       hb_free (this->accels);
       this->table.destroy ();
       this->table.destroy ();
     }
     }
@@ -4869,6 +4937,7 @@ struct GSUBGPOS
 
 
 	if (unlikely (!accels[lookup_index].cmpexch (nullptr, accel)))
 	if (unlikely (!accels[lookup_index].cmpexch (nullptr, accel)))
 	{
 	{
+	  accel->fini ();
 	  hb_free (accel);
 	  hb_free (accel);
 	  goto retry;
 	  goto retry;
 	}
 	}

+ 22 - 8
thirdparty/harfbuzz/src/hb-ot-layout.cc

@@ -246,6 +246,18 @@ OT::GDEF::is_blocklisted (hb_blob_t *blob,
     /* sha1sum: c26e41d567ed821bed997e937bc0c41435689e85  Padauk.ttf
     /* sha1sum: c26e41d567ed821bed997e937bc0c41435689e85  Padauk.ttf
      *  "Padauk Regular" "Version 2.5", see https://crbug.com/681813 */
      *  "Padauk Regular" "Version 2.5", see https://crbug.com/681813 */
     case HB_CODEPOINT_ENCODE3 (1004, 59092, 14836):
     case HB_CODEPOINT_ENCODE3 (1004, 59092, 14836):
+    /* 88d2006ca084f04af2df1954ed714a8c71e8400f  Courier New.ttf from macOS 15 */
+    case HB_CODEPOINT_ENCODE3 (588, 5078, 14418):
+    /* 608e3ebb6dd1aee521cff08eb07d500a2c59df68  Courier New Bold.ttf from macOS 15 */
+    case HB_CODEPOINT_ENCODE3 (588, 5078, 14238):
+    /* d13221044ff054efd78f1cd8631b853c3ce85676  cour.ttf from Windows 10 */
+    case HB_CODEPOINT_ENCODE3 (894, 17162, 33960):
+    /* 68ed4a22d8067fcf1622ac6f6e2f4d3a2e3ec394  courbd.ttf from Windows 10 */
+    case HB_CODEPOINT_ENCODE3 (894, 17154, 34472):
+    /* 4cdb0259c96b7fd7c103821bb8f08f7cc6b211d7  cour.ttf from Windows 8.1 */
+    case HB_CODEPOINT_ENCODE3 (816, 7868, 17052):
+    /* 920483d8a8ed37f7f0afdabbe7f679aece7c75d8  courbd.ttf from Windows 8.1 */
+    case HB_CODEPOINT_ENCODE3 (816, 7868, 17138):
       return true;
       return true;
   }
   }
   return false;
   return false;
@@ -1911,9 +1923,10 @@ apply_forward (OT::hb_ot_apply_context_t *c,
   while (buffer->idx < buffer->len && buffer->successful)
   while (buffer->idx < buffer->len && buffer->successful)
   {
   {
     bool applied = false;
     bool applied = false;
-    if (accel.digest.may_have (buffer->cur().codepoint) &&
-	(buffer->cur().mask & c->lookup_mask) &&
-	c->check_glyph_property (&buffer->cur(), c->lookup_props))
+    auto &cur = buffer->cur();
+    if (accel.digest.may_have (cur.codepoint) &&
+	(cur.mask & c->lookup_mask) &&
+	c->check_glyph_property (&cur, c->lookup_props))
      {
      {
        applied = accel.apply (c, subtable_count, use_cache);
        applied = accel.apply (c, subtable_count, use_cache);
      }
      }
@@ -1939,9 +1952,10 @@ apply_backward (OT::hb_ot_apply_context_t *c,
   hb_buffer_t *buffer = c->buffer;
   hb_buffer_t *buffer = c->buffer;
   do
   do
   {
   {
-    if (accel.digest.may_have (buffer->cur().codepoint) &&
-	(buffer->cur().mask & c->lookup_mask) &&
-	c->check_glyph_property (&buffer->cur(), c->lookup_props))
+    auto &cur = buffer->cur();
+    if (accel.digest.may_have (cur.codepoint) &&
+	(cur.mask & c->lookup_mask) &&
+	c->check_glyph_property (&cur, c->lookup_props))
       ret |= accel.apply (c, subtable_count, false);
       ret |= accel.apply (c, subtable_count, false);
 
 
     /* The reverse lookup doesn't "advance" cursor (for good reason). */
     /* The reverse lookup doesn't "advance" cursor (for good reason). */
@@ -2021,7 +2035,7 @@ inline void hb_ot_map_t::apply (const Proxy &proxy,
        * (plus some past glyphs).
        * (plus some past glyphs).
        *
        *
        * Only try applying the lookup if there is any overlap. */
        * Only try applying the lookup if there is any overlap. */
-      if (accel->digest.may_have (c.digest))
+      if (accel->digest.may_intersect (c.digest))
       {
       {
 	c.set_lookup_index (lookup_index);
 	c.set_lookup_index (lookup_index);
 	c.set_lookup_mask (lookup.mask, false);
 	c.set_lookup_mask (lookup.mask, false);
@@ -2047,7 +2061,7 @@ inline void hb_ot_map_t::apply (const Proxy &proxy,
       if (stage->pause_func (plan, font, buffer))
       if (stage->pause_func (plan, font, buffer))
       {
       {
 	/* Refresh working buffer digest since buffer changed. */
 	/* Refresh working buffer digest since buffer changed. */
-	c.digest = buffer->digest ();
+	buffer->collect_codepoints (c.digest);
       }
       }
     }
     }
   }
   }

+ 7 - 3
thirdparty/harfbuzz/src/hb-ot-layout.hh

@@ -339,6 +339,11 @@ _hb_glyph_info_is_default_ignorable (const hb_glyph_info_t *info)
 	 !_hb_glyph_info_substituted (info);
 	 !_hb_glyph_info_substituted (info);
 }
 }
 static inline void
 static inline void
+_hb_glyph_info_set_default_ignorable (hb_glyph_info_t *info)
+{
+  info->unicode_props() |= UPROPS_MASK_IGNORABLE;
+}
+static inline void
 _hb_glyph_info_clear_default_ignorable (hb_glyph_info_t *info)
 _hb_glyph_info_clear_default_ignorable (hb_glyph_info_t *info)
 {
 {
   info->unicode_props() &= ~ UPROPS_MASK_IGNORABLE;
   info->unicode_props() &= ~ UPROPS_MASK_IGNORABLE;
@@ -360,7 +365,7 @@ _hb_glyph_info_set_continuation (hb_glyph_info_t *info)
   info->unicode_props() |= UPROPS_MASK_CONTINUATION;
   info->unicode_props() |= UPROPS_MASK_CONTINUATION;
 }
 }
 static inline void
 static inline void
-_hb_glyph_info_reset_continuation (hb_glyph_info_t *info)
+_hb_glyph_info_clear_continuation (hb_glyph_info_t *info)
 {
 {
   info->unicode_props() &= ~ UPROPS_MASK_CONTINUATION;
   info->unicode_props() &= ~ UPROPS_MASK_CONTINUATION;
 }
 }
@@ -633,8 +638,7 @@ _hb_buffer_assert_gsubgpos_vars (hb_buffer_t *buffer)
 }
 }
 
 
 /* Make sure no one directly touches our props... */
 /* Make sure no one directly touches our props... */
-#undef unicode_props0
-#undef unicode_props1
+#undef unicode_props
 #undef lig_props
 #undef lig_props
 #undef glyph_props
 #undef glyph_props
 
 

+ 14 - 0
thirdparty/harfbuzz/src/hb-ot-map.cc

@@ -390,5 +390,19 @@ hb_ot_map_builder_t::compile (hb_ot_map_t                  &m,
   }
   }
 }
 }
 
 
+unsigned int hb_ot_map_t::get_feature_tags (unsigned int start_offset, unsigned int *tag_count, hb_tag_t *tags) const
+{
+  if (tag_count)
+  {
+    auto sub_features = features.as_array ().sub_array (start_offset, tag_count);
+    if (tags)
+    {
+      for (unsigned int i = 0; i < sub_features.length; i++)
+        tags[i] = sub_features[i].tag;
+    }
+  }
+
+  return features.length;
+}
 
 
 #endif
 #endif

+ 3 - 0
thirdparty/harfbuzz/src/hb-ot-map.hh

@@ -166,6 +166,9 @@ struct hb_ot_map_t
 			  const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const;
 			  const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const;
   HB_INTERNAL void substitute (const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const;
   HB_INTERNAL void substitute (const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const;
   HB_INTERNAL void position (const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const;
   HB_INTERNAL void position (const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const;
+  HB_INTERNAL unsigned int get_feature_tags (unsigned int  start_offset,
+					     unsigned int *tag_count, /* IN/OUT */
+					     hb_tag_t     *tags /* OUT */) const;
 
 
   public:
   public:
   hb_tag_t chosen_script[2];
   hb_tag_t chosen_script[2];

+ 2 - 2
thirdparty/harfbuzz/src/hb-ot-os2-table.hh

@@ -284,8 +284,8 @@ struct OS2
         os2_prime->usWidthClass = width_class;
         os2_prime->usWidthClass = width_class;
     }
     }
 
 
-    os2_prime->usFirstCharIndex = hb_min (0xFFFFu, c->plan->unicodes.get_min ());
-    os2_prime->usLastCharIndex  = hb_min (0xFFFFu, c->plan->unicodes.get_max ());
+    os2_prime->usFirstCharIndex = hb_min (0xFFFFu, c->plan->os2_info.min_cmap_codepoint);
+    os2_prime->usLastCharIndex  = hb_min (0xFFFFu, c->plan->os2_info.max_cmap_codepoint);
 
 
     if (c->plan->flags & HB_SUBSET_FLAGS_NO_PRUNE_UNICODE_RANGES)
     if (c->plan->flags & HB_SUBSET_FLAGS_NO_PRUNE_UNICODE_RANGES)
       return_trace (true);
       return_trace (true);

+ 32 - 19
thirdparty/harfbuzz/src/hb-ot-shape.cc

@@ -46,6 +46,8 @@
 #include "hb-set.hh"
 #include "hb-set.hh"
 
 
 #include "hb-aat-layout.hh"
 #include "hb-aat-layout.hh"
+#include "hb-ot-stat-table.hh"
+
 
 
 static inline bool
 static inline bool
 _hb_codepoint_is_regional_indicator (hb_codepoint_t u)
 _hb_codepoint_is_regional_indicator (hb_codepoint_t u)
@@ -121,10 +123,6 @@ hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t           &plan,
   plan.kern_mask = plan.map.get_mask (kern_tag);
   plan.kern_mask = plan.map.get_mask (kern_tag);
   plan.requested_kerning = !!plan.kern_mask;
   plan.requested_kerning = !!plan.kern_mask;
 #endif
 #endif
-#ifndef HB_NO_AAT_SHAPE
-  plan.trak_mask = plan.map.get_mask (HB_TAG ('t','r','a','k'));
-  plan.requested_tracking = !!plan.trak_mask;
-#endif
 
 
   bool has_gpos_kern = plan.map.get_feature_index (1, kern_tag) != HB_OT_LAYOUT_NO_FEATURE_INDEX;
   bool has_gpos_kern = plan.map.get_feature_index (1, kern_tag) != HB_OT_LAYOUT_NO_FEATURE_INDEX;
   bool disable_gpos = plan.shaper->gpos_tag &&
   bool disable_gpos = plan.shaper->gpos_tag &&
@@ -207,9 +205,6 @@ hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t           &plan,
      https://github.com/harfbuzz/harfbuzz/issues/2967. */
      https://github.com/harfbuzz/harfbuzz/issues/2967. */
   if (plan.apply_morx)
   if (plan.apply_morx)
     plan.adjust_mark_positioning_when_zeroing = false;
     plan.adjust_mark_positioning_when_zeroing = false;
-
-  /* Currently we always apply trak. */
-  plan.apply_trak = plan.requested_tracking && hb_aat_layout_has_tracking (face);
 #endif
 #endif
 }
 }
 
 
@@ -274,11 +269,6 @@ hb_ot_shape_plan_t::position (hb_font_t   *font,
 #endif
 #endif
   else if (this->apply_fallback_kern)
   else if (this->apply_fallback_kern)
     _hb_ot_shape_fallback_kern (this, font, buffer);
     _hb_ot_shape_fallback_kern (this, font, buffer);
-
-#ifndef HB_NO_AAT_SHAPE
-  if (this->apply_trak)
-    hb_aat_layout_track (this, font, buffer);
-#endif
 }
 }
 
 
 
 
@@ -346,13 +336,6 @@ hb_ot_shape_collect_features (hb_ot_shape_planner_t *planner,
   /* Random! */
   /* Random! */
   map->enable_feature (HB_TAG ('r','a','n','d'), F_RANDOM, HB_OT_MAP_MAX_VALUE);
   map->enable_feature (HB_TAG ('r','a','n','d'), F_RANDOM, HB_OT_MAP_MAX_VALUE);
 
 
-#ifndef HB_NO_AAT_SHAPE
-  /* Tracking.  We enable dummy feature here just to allow disabling
-   * AAT 'trak' table using features.
-   * https://github.com/harfbuzz/harfbuzz/issues/1303 */
-  map->enable_feature (HB_TAG ('t','r','a','k'), F_HAS_FALLBACK);
-#endif
-
   map->enable_feature (HB_TAG ('H','a','r','f')); /* Considered required. */
   map->enable_feature (HB_TAG ('H','a','r','f')); /* Considered required. */
   map->enable_feature (HB_TAG ('H','A','R','F')); /* Considered discretionary. */
   map->enable_feature (HB_TAG ('H','A','R','F')); /* Considered discretionary. */
 
 
@@ -1277,6 +1260,36 @@ hb_ot_shape_plan_collect_lookups (hb_shape_plan_t *shape_plan,
 }
 }
 
 
 
 
+/**
+ * hb_ot_shape_plan_get_feature_tags:
+ * @shape_plan: A shaping plan
+ * @start_offset: The index of first feature to retrieve
+ * @tag_count: (inout): Input = the maximum number of features to return;
+ *                      Output = the actual number of features returned (may be zero)
+ * @tags: (out) (array length=tag_count): The array of enabled feature
+ *
+ * Fetches the list of OpenType feature tags enabled for a shaping plan, if possible.
+ *
+ * Return value: Total number of feature tagss.
+ *
+ * Since: 10.3.0
+ */
+unsigned int
+hb_ot_shape_plan_get_feature_tags (hb_shape_plan_t *shape_plan,
+				   unsigned int     start_offset,
+				   unsigned int    *tag_count, /* IN/OUT */
+				   hb_tag_t        *tags /* OUT */)
+{
+#ifndef HB_NO_OT_SHAPE
+  return shape_plan->ot.map.get_feature_tags (start_offset, tag_count, tags);
+#else
+  if (tag_count)
+	*tag_count = 0;
+  return 0;
+#endif
+}
+
+
 /* TODO Move this to hb-ot-shape-normalize, make it do decompose, and make it public. */
 /* TODO Move this to hb-ot-shape-normalize, make it do decompose, and make it public. */
 static void
 static void
 add_char (hb_font_t          *font,
 add_char (hb_font_t          *font,

+ 6 - 0
thirdparty/harfbuzz/src/hb-ot-shape.h

@@ -48,6 +48,12 @@ hb_ot_shape_plan_collect_lookups (hb_shape_plan_t *shape_plan,
 				  hb_tag_t         table_tag,
 				  hb_tag_t         table_tag,
 				  hb_set_t        *lookup_indexes /* OUT */);
 				  hb_set_t        *lookup_indexes /* OUT */);
 
 
+HB_EXTERN unsigned int
+hb_ot_shape_plan_get_feature_tags (hb_shape_plan_t *shape_plan,
+				   unsigned int     start_offset,
+				   unsigned int    *tag_count, /* IN/OUT */
+				   hb_tag_t        *tags /* OUT */);
+
 HB_END_DECLS
 HB_END_DECLS
 
 
 #endif /* HB_OT_SHAPE_H */
 #endif /* HB_OT_SHAPE_H */

+ 2 - 13
thirdparty/harfbuzz/src/hb-ot-shape.hh

@@ -51,7 +51,8 @@ struct hb_ot_shape_plan_key_t
 
 
   bool equal (const hb_ot_shape_plan_key_t *other)
   bool equal (const hb_ot_shape_plan_key_t *other)
   {
   {
-    return 0 == hb_memcmp (this, other, sizeof (*this));
+    return variations_index[0] == other->variations_index[0] &&
+	   variations_index[1] == other->variations_index[1];
   }
   }
 };
 };
 
 
@@ -79,22 +80,12 @@ struct hb_ot_shape_plan_t
 #else
 #else
   static constexpr hb_mask_t kern_mask = 0;
   static constexpr hb_mask_t kern_mask = 0;
 #endif
 #endif
-#ifndef HB_NO_AAT_SHAPE
-  hb_mask_t trak_mask;
-#else
-  static constexpr hb_mask_t trak_mask = 0;
-#endif
 
 
 #ifndef HB_NO_OT_KERN
 #ifndef HB_NO_OT_KERN
   bool requested_kerning : 1;
   bool requested_kerning : 1;
 #else
 #else
   static constexpr bool requested_kerning = false;
   static constexpr bool requested_kerning = false;
 #endif
 #endif
-#ifndef HB_NO_AAT_SHAPE
-  bool requested_tracking : 1;
-#else
-  static constexpr bool requested_tracking = false;
-#endif
 #ifndef HB_NO_OT_SHAPE_FRACTIONS
 #ifndef HB_NO_OT_SHAPE_FRACTIONS
   bool has_frac : 1;
   bool has_frac : 1;
 #else
 #else
@@ -117,11 +108,9 @@ struct hb_ot_shape_plan_t
 #ifndef HB_NO_AAT_SHAPE
 #ifndef HB_NO_AAT_SHAPE
   bool apply_kerx : 1;
   bool apply_kerx : 1;
   bool apply_morx : 1;
   bool apply_morx : 1;
-  bool apply_trak : 1;
 #else
 #else
   static constexpr bool apply_kerx = false;
   static constexpr bool apply_kerx = false;
   static constexpr bool apply_morx = false;
   static constexpr bool apply_morx = false;
-  static constexpr bool apply_trak = false;
 #endif
 #endif
 
 
   void collect_lookups (hb_tag_t table_tag, hb_set_t *lookups) const
   void collect_lookups (hb_tag_t table_tag, hb_set_t *lookups) const

+ 2 - 0
thirdparty/harfbuzz/src/hb-ot-shaper-arabic-fallback.hh

@@ -355,6 +355,8 @@ arabic_fallback_plan_destroy (arabic_fallback_plan_t *fallback_plan)
   for (unsigned int i = 0; i < fallback_plan->num_lookups; i++)
   for (unsigned int i = 0; i < fallback_plan->num_lookups; i++)
     if (fallback_plan->lookup_array[i])
     if (fallback_plan->lookup_array[i])
     {
     {
+      if (fallback_plan->accel_array[i])
+	fallback_plan->accel_array[i]->fini ();
       hb_free (fallback_plan->accel_array[i]);
       hb_free (fallback_plan->accel_array[i]);
       if (fallback_plan->free_lookups)
       if (fallback_plan->free_lookups)
 	hb_free (fallback_plan->lookup_array[i]);
 	hb_free (fallback_plan->lookup_array[i]);

文件差异内容过多而无法显示
+ 896 - 230
thirdparty/harfbuzz/src/hb-ot-shaper-indic-machine.hh


+ 7 - 4
thirdparty/harfbuzz/src/hb-ot-shaper-indic-table.cc

@@ -48,6 +48,7 @@
 #define OT_CM I_Cat(CM)
 #define OT_CM I_Cat(CM)
 #define OT_Symbol I_Cat(Symbol)
 #define OT_Symbol I_Cat(Symbol)
 #define OT_CS I_Cat(CS)
 #define OT_CS I_Cat(CS)
+#define OT_SMPst I_Cat(SMPst)
 /* khmer */
 /* khmer */
 #define OT_VAbv K_Cat(VAbv)
 #define OT_VAbv K_Cat(VAbv)
 #define OT_VBlw K_Cat(VBlw)
 #define OT_VBlw K_Cat(VBlw)
@@ -94,7 +95,8 @@ static_assert (OT_VPst == M_Cat(VPst), "");
 #define _OT_R    OT_Ra           /*  14 chars; Ra */
 #define _OT_R    OT_Ra           /*  14 chars; Ra */
 #define _OT_Rf   OT_Repha        /*   1 chars; Repha */
 #define _OT_Rf   OT_Repha        /*   1 chars; Repha */
 #define _OT_Rt   OT_Robatic      /*   3 chars; Robatic */
 #define _OT_Rt   OT_Robatic      /*   3 chars; Robatic */
-#define _OT_SM   OT_SM           /*  56 chars; SM */
+#define _OT_SM   OT_SM           /*  50 chars; SM */
+#define _OT_SP   OT_SMPst        /*   6 chars; SMPst */
 #define _OT_S    OT_Symbol       /*  22 chars; Symbol */
 #define _OT_S    OT_Symbol       /*  22 chars; Symbol */
 #define _OT_V    OT_V            /* 172 chars; V */
 #define _OT_V    OT_V            /* 172 chars; V */
 #define _OT_VA   OT_VAbv         /*  18 chars; VAbv */
 #define _OT_VA   OT_VAbv         /*  18 chars; VAbv */
@@ -145,7 +147,7 @@ static const uint16_t indic_table[] = {
 
 
   /* Latin-1 Supplement */
   /* Latin-1 Supplement */
 
 
-  /* 00B0 */  _(X,X),  _(X,X),_(SM,SM),_(SM,SM),  _(X,X),  _(X,X),  _(X,X),  _(X,X),
+  /* 00B0 */  _(X,X),  _(X,X),_(SP,SM),_(SP,SM),  _(X,X),  _(X,X),  _(X,X),  _(X,X),
   /* 00B8 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),
   /* 00B8 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),
   /* 00C0 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),
   /* 00C0 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),
   /* 00C8 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),
   /* 00C8 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),
@@ -398,9 +400,9 @@ static const uint16_t indic_table[] = {
 
 
   /* Superscripts and Subscripts */
   /* Superscripts and Subscripts */
 
 
-  /* 2070 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),_(SM,SM),  _(X,X),  _(X,X),  _(X,X),
+  /* 2070 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),_(SP,SM),  _(X,X),  _(X,X),  _(X,X),
   /* 2078 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),
   /* 2078 */  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),  _(X,X),
-  /* 2080 */  _(X,X),  _(X,X),_(SM,SM),_(SM,SM),_(SM,SM),  _(X,X),  _(X,X),  _(X,X),
+  /* 2080 */  _(X,X),  _(X,X),_(SP,SM),_(SP,SM),_(SP,SM),  _(X,X),  _(X,X),  _(X,X),
 
 
 #define indic_offset_0x25f8u 1592
 #define indic_offset_0x25f8u 1592
 
 
@@ -540,6 +542,7 @@ hb_indic_get_categories (hb_codepoint_t u)
 #undef _OT_Rf
 #undef _OT_Rf
 #undef _OT_Rt
 #undef _OT_Rt
 #undef _OT_SM
 #undef _OT_SM
+#undef _OT_SP
 #undef _OT_S
 #undef _OT_S
 #undef _OT_V
 #undef _OT_V
 #undef _OT_VA
 #undef _OT_VA

+ 386 - 256
thirdparty/harfbuzz/src/hb-ot-shaper-myanmar-machine.hh

@@ -68,6 +68,7 @@ enum myanmar_syllable_type_t {
 #define myanmar_syllable_machine_ex_PT 39u
 #define myanmar_syllable_machine_ex_PT 39u
 #define myanmar_syllable_machine_ex_Ra 15u
 #define myanmar_syllable_machine_ex_Ra 15u
 #define myanmar_syllable_machine_ex_SM 8u
 #define myanmar_syllable_machine_ex_SM 8u
+#define myanmar_syllable_machine_ex_SMPst 57u
 #define myanmar_syllable_machine_ex_VAbv 20u
 #define myanmar_syllable_machine_ex_VAbv 20u
 #define myanmar_syllable_machine_ex_VBlw 21u
 #define myanmar_syllable_machine_ex_VBlw 21u
 #define myanmar_syllable_machine_ex_VPre 22u
 #define myanmar_syllable_machine_ex_VPre 22u
@@ -77,35 +78,35 @@ enum myanmar_syllable_type_t {
 #define myanmar_syllable_machine_ex_ZWNJ 5u
 #define myanmar_syllable_machine_ex_ZWNJ 5u
 
 
 
 
-#line 81 "hb-ot-shaper-myanmar-machine.hh"
+#line 82 "hb-ot-shaper-myanmar-machine.hh"
 static const unsigned char _myanmar_syllable_machine_trans_keys[] = {
 static const unsigned char _myanmar_syllable_machine_trans_keys[] = {
-	1u, 41u, 3u, 41u, 5u, 39u, 5u, 8u, 3u, 41u, 3u, 39u, 3u, 39u, 5u, 39u, 
-	5u, 39u, 3u, 39u, 3u, 39u, 3u, 41u, 5u, 39u, 1u, 15u, 3u, 39u, 3u, 39u, 
-	3u, 40u, 3u, 39u, 3u, 41u, 3u, 41u, 3u, 39u, 3u, 41u, 3u, 41u, 3u, 41u, 
-	3u, 41u, 3u, 41u, 5u, 39u, 5u, 8u, 3u, 41u, 3u, 39u, 3u, 39u, 5u, 39u, 
-	5u, 39u, 3u, 39u, 3u, 39u, 3u, 41u, 5u, 39u, 1u, 15u, 3u, 41u, 3u, 39u, 
-	3u, 39u, 3u, 40u, 3u, 39u, 3u, 41u, 3u, 41u, 3u, 39u, 3u, 41u, 3u, 41u, 
-	3u, 41u, 3u, 41u, 3u, 41u, 3u, 41u, 3u, 41u, 1u, 41u, 1u, 15u, 0
+	1u, 57u, 3u, 57u, 5u, 57u, 5u, 57u, 3u, 57u, 5u, 57u, 3u, 57u, 3u, 57u, 
+	3u, 57u, 3u, 57u, 3u, 57u, 5u, 57u, 1u, 15u, 3u, 57u, 3u, 57u, 3u, 57u, 
+	3u, 57u, 3u, 57u, 3u, 57u, 3u, 57u, 3u, 57u, 3u, 57u, 3u, 57u, 3u, 57u, 
+	3u, 57u, 5u, 57u, 5u, 57u, 3u, 57u, 5u, 57u, 3u, 57u, 3u, 57u, 3u, 57u, 
+	3u, 57u, 3u, 57u, 5u, 57u, 1u, 15u, 3u, 57u, 3u, 57u, 3u, 57u, 3u, 57u, 
+	3u, 57u, 3u, 57u, 3u, 57u, 3u, 57u, 3u, 57u, 3u, 57u, 3u, 57u, 3u, 57u, 
+	3u, 57u, 3u, 57u, 3u, 57u, 1u, 57u, 1u, 15u, 0
 };
 };
 
 
 static const char _myanmar_syllable_machine_key_spans[] = {
 static const char _myanmar_syllable_machine_key_spans[] = {
-	41, 39, 35, 4, 39, 37, 37, 35, 
-	35, 37, 37, 39, 35, 15, 37, 37, 
-	38, 37, 39, 39, 37, 39, 39, 39, 
-	39, 39, 35, 4, 39, 37, 37, 35, 
-	35, 37, 37, 39, 35, 15, 39, 37, 
-	37, 38, 37, 39, 39, 37, 39, 39, 
-	39, 39, 39, 39, 39, 41, 15
+	57, 55, 53, 53, 55, 53, 55, 55, 
+	55, 55, 55, 53, 15, 55, 55, 55, 
+	55, 55, 55, 55, 55, 55, 55, 55, 
+	55, 53, 53, 55, 53, 55, 55, 55, 
+	55, 55, 53, 15, 55, 55, 55, 55, 
+	55, 55, 55, 55, 55, 55, 55, 55, 
+	55, 55, 55, 57, 15
 };
 };
 
 
 static const short _myanmar_syllable_machine_index_offsets[] = {
 static const short _myanmar_syllable_machine_index_offsets[] = {
-	0, 42, 82, 118, 123, 163, 201, 239, 
-	275, 311, 349, 387, 427, 463, 479, 517, 
-	555, 594, 632, 672, 712, 750, 790, 830, 
-	870, 910, 950, 986, 991, 1031, 1069, 1107, 
-	1143, 1179, 1217, 1255, 1295, 1331, 1347, 1387, 
-	1425, 1463, 1502, 1540, 1580, 1620, 1658, 1698, 
-	1738, 1778, 1818, 1858, 1898, 1938, 1980
+	0, 58, 114, 168, 222, 278, 332, 388, 
+	444, 500, 556, 612, 666, 682, 738, 794, 
+	850, 906, 962, 1018, 1074, 1130, 1186, 1242, 
+	1298, 1354, 1408, 1462, 1518, 1572, 1628, 1684, 
+	1740, 1796, 1852, 1906, 1922, 1978, 2034, 2090, 
+	2146, 2202, 2258, 2314, 2370, 2426, 2482, 2538, 
+	2594, 2650, 2706, 2762, 2820
 };
 };
 
 
 static const char _myanmar_syllable_machine_indicies[] = {
 static const char _myanmar_syllable_machine_indicies[] = {
@@ -114,273 +115,378 @@ static const char _myanmar_syllable_machine_indicies[] = {
 	0, 8, 0, 9, 10, 11, 12, 0, 
 	0, 8, 0, 9, 10, 11, 12, 0, 
 	0, 0, 0, 0, 0, 0, 0, 13, 
 	0, 0, 0, 0, 0, 0, 0, 13, 
 	0, 0, 14, 15, 16, 17, 18, 19, 
 	0, 0, 14, 15, 16, 17, 18, 19, 
-	20, 0, 22, 23, 24, 24, 21, 25, 
-	26, 21, 21, 21, 21, 21, 21, 21, 
-	21, 21, 21, 27, 28, 29, 30, 21, 
-	21, 21, 21, 21, 21, 21, 21, 31, 
-	21, 21, 32, 33, 34, 35, 36, 37, 
-	38, 21, 24, 24, 21, 25, 21, 21, 
-	21, 21, 21, 21, 21, 21, 21, 21, 
-	21, 21, 21, 21, 30, 21, 21, 21, 
-	21, 21, 21, 21, 21, 39, 21, 21, 
-	21, 21, 21, 21, 36, 21, 24, 24, 
-	21, 25, 21, 22, 21, 24, 24, 21, 
-	25, 26, 21, 21, 21, 21, 21, 21, 
-	21, 21, 21, 21, 40, 21, 21, 30, 
-	21, 21, 21, 21, 21, 21, 21, 21, 
-	41, 21, 21, 42, 21, 21, 21, 36, 
-	21, 41, 21, 22, 21, 24, 24, 21, 
-	25, 26, 21, 21, 21, 21, 21, 21, 
-	21, 21, 21, 21, 21, 21, 21, 30, 
-	21, 21, 21, 21, 21, 21, 21, 21, 
-	21, 21, 21, 21, 21, 21, 21, 36, 
-	21, 43, 21, 24, 24, 21, 25, 36, 
-	21, 21, 21, 21, 21, 21, 21, 21, 
-	21, 21, 21, 21, 21, 21, 21, 21, 
-	21, 21, 21, 21, 21, 21, 44, 21, 
-	21, 21, 21, 21, 21, 36, 21, 24, 
-	24, 21, 25, 21, 21, 21, 21, 21, 
-	21, 21, 21, 21, 21, 21, 21, 21, 
-	21, 21, 21, 21, 21, 21, 21, 21, 
-	21, 21, 44, 21, 21, 21, 21, 21, 
-	21, 36, 21, 24, 24, 21, 25, 21, 
-	21, 21, 21, 21, 21, 21, 21, 21, 
-	21, 21, 21, 21, 21, 21, 21, 21, 
-	21, 21, 21, 21, 21, 21, 21, 21, 
-	21, 21, 21, 21, 21, 36, 21, 22, 
-	21, 24, 24, 21, 25, 26, 21, 21, 
-	21, 21, 21, 21, 21, 21, 21, 21, 
-	40, 21, 21, 30, 21, 21, 21, 21, 
-	21, 21, 21, 21, 21, 21, 21, 21, 
-	21, 21, 21, 36, 21, 22, 21, 24, 
-	24, 21, 25, 26, 21, 21, 21, 21, 
-	21, 21, 21, 21, 21, 21, 40, 21, 
-	21, 30, 21, 21, 21, 21, 21, 21, 
-	21, 21, 41, 21, 21, 21, 21, 21, 
-	21, 36, 21, 22, 21, 24, 24, 21, 
-	25, 26, 21, 21, 21, 21, 21, 21, 
-	21, 21, 21, 21, 40, 21, 21, 30, 
-	21, 21, 21, 21, 21, 21, 21, 21, 
-	41, 21, 21, 21, 21, 21, 21, 36, 
-	21, 41, 21, 24, 24, 21, 25, 21, 
-	21, 21, 21, 21, 21, 21, 21, 21, 
-	21, 21, 21, 21, 21, 30, 21, 21, 
-	21, 21, 21, 21, 21, 21, 21, 21, 
-	21, 21, 21, 21, 21, 36, 21, 1, 
-	1, 21, 21, 21, 21, 21, 21, 21, 
-	21, 21, 21, 21, 21, 1, 21, 22, 
-	21, 24, 24, 21, 25, 26, 21, 21, 
-	21, 21, 21, 21, 21, 21, 21, 21, 
-	27, 28, 21, 30, 21, 21, 21, 21, 
-	21, 21, 21, 21, 21, 21, 21, 21, 
-	21, 21, 21, 36, 21, 22, 21, 24, 
-	24, 21, 25, 26, 21, 21, 21, 21, 
-	21, 21, 21, 21, 21, 21, 21, 28, 
-	21, 30, 21, 21, 21, 21, 21, 21, 
-	21, 21, 21, 21, 21, 21, 21, 21, 
-	21, 36, 21, 22, 21, 24, 24, 21, 
-	25, 26, 21, 21, 21, 21, 21, 21, 
-	21, 21, 21, 21, 27, 28, 29, 30, 
-	21, 21, 21, 21, 21, 21, 21, 21, 
-	21, 21, 21, 21, 21, 21, 21, 36, 
-	45, 21, 22, 21, 24, 24, 21, 25, 
-	26, 21, 21, 21, 21, 21, 21, 21, 
-	21, 21, 21, 27, 28, 29, 30, 21, 
-	21, 21, 21, 21, 21, 21, 21, 21, 
-	21, 21, 21, 21, 21, 21, 36, 21, 
-	22, 21, 24, 24, 21, 25, 26, 21, 
-	21, 21, 21, 21, 21, 21, 21, 21, 
-	21, 27, 28, 29, 30, 21, 21, 21, 
-	21, 21, 21, 21, 21, 31, 21, 21, 
-	32, 33, 34, 35, 36, 21, 38, 21, 
-	22, 21, 24, 24, 21, 25, 26, 21, 
-	21, 21, 21, 21, 21, 21, 21, 21, 
-	21, 27, 28, 29, 30, 21, 21, 21, 
-	21, 21, 21, 21, 21, 45, 21, 21, 
-	21, 21, 21, 21, 36, 21, 38, 21, 
-	22, 21, 24, 24, 21, 25, 26, 21, 
-	21, 21, 21, 21, 21, 21, 21, 21, 
-	21, 27, 28, 29, 30, 21, 21, 21, 
-	21, 21, 21, 21, 21, 45, 21, 21, 
-	21, 21, 21, 21, 36, 21, 22, 21, 
-	24, 24, 21, 25, 26, 21, 21, 21, 
-	21, 21, 21, 21, 21, 21, 21, 27, 
-	28, 29, 30, 21, 21, 21, 21, 21, 
-	21, 21, 21, 21, 21, 21, 32, 21, 
-	34, 21, 36, 21, 38, 21, 22, 21, 
-	24, 24, 21, 25, 26, 21, 21, 21, 
-	21, 21, 21, 21, 21, 21, 21, 27, 
-	28, 29, 30, 21, 21, 21, 21, 21, 
-	21, 21, 21, 45, 21, 21, 32, 21, 
-	21, 21, 36, 21, 38, 21, 22, 21, 
-	24, 24, 21, 25, 26, 21, 21, 21, 
-	21, 21, 21, 21, 21, 21, 21, 27, 
-	28, 29, 30, 21, 21, 21, 21, 21, 
-	21, 21, 21, 46, 21, 21, 32, 33, 
-	34, 21, 36, 21, 38, 21, 22, 21, 
-	24, 24, 21, 25, 26, 21, 21, 21, 
-	21, 21, 21, 21, 21, 21, 21, 27, 
-	28, 29, 30, 21, 21, 21, 21, 21, 
-	21, 21, 21, 21, 21, 21, 32, 33, 
-	34, 21, 36, 21, 38, 21, 22, 23, 
-	24, 24, 21, 25, 26, 21, 21, 21, 
-	21, 21, 21, 21, 21, 21, 21, 27, 
-	28, 29, 30, 21, 21, 21, 21, 21, 
-	21, 21, 21, 31, 21, 21, 32, 33, 
-	34, 35, 36, 21, 38, 21, 48, 48, 
+	20, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	21, 0, 23, 24, 25, 25, 22, 26, 
+	27, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 28, 29, 30, 31, 22, 
+	22, 22, 22, 22, 22, 22, 22, 32, 
+	22, 22, 33, 34, 35, 36, 37, 38, 
+	39, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	26, 22, 25, 25, 22, 26, 22, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 31, 22, 22, 22, 
+	22, 22, 22, 22, 22, 40, 22, 22, 
+	22, 22, 22, 22, 37, 22, 22, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 22, 26, 22, 
+	25, 25, 22, 26, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 37, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 26, 22, 41, 22, 
+	25, 25, 22, 26, 37, 22, 22, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 26, 22, 22, 22, 22, 
+	22, 22, 37, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 26, 22, 25, 25, 
+	22, 26, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	22, 26, 22, 22, 22, 22, 22, 22, 
+	37, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 26, 22, 23, 22, 25, 25, 
+	22, 26, 27, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 42, 22, 22, 
+	31, 22, 22, 22, 22, 22, 22, 22, 
+	22, 43, 22, 22, 44, 22, 22, 22, 
+	37, 22, 43, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 26, 22, 23, 22, 25, 25, 
+	22, 26, 27, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	31, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	37, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 26, 22, 23, 22, 25, 25, 
+	22, 26, 27, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 42, 22, 22, 
+	31, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	37, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 26, 22, 23, 22, 25, 25, 
+	22, 26, 27, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 42, 22, 22, 
+	31, 22, 22, 22, 22, 22, 22, 22, 
+	22, 43, 22, 22, 22, 22, 22, 22, 
+	37, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 26, 22, 23, 22, 25, 25, 
+	22, 26, 27, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 42, 22, 22, 
+	31, 22, 22, 22, 22, 22, 22, 22, 
+	22, 43, 22, 22, 22, 22, 22, 22, 
+	37, 22, 43, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 26, 22, 25, 25, 22, 26, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 22, 31, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 22, 37, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	26, 22, 1, 1, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	1, 22, 23, 22, 25, 25, 22, 26, 
+	27, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 28, 29, 22, 31, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 22, 37, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	26, 22, 23, 22, 25, 25, 22, 26, 
+	27, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 29, 22, 31, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 22, 37, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	26, 22, 23, 22, 25, 25, 22, 26, 
+	27, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 28, 29, 30, 31, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 22, 37, 45, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	26, 22, 23, 22, 25, 25, 22, 26, 
+	27, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 28, 29, 30, 31, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 22, 37, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	26, 22, 23, 22, 25, 25, 22, 26, 
+	27, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 28, 29, 30, 31, 22, 
+	22, 22, 22, 22, 22, 22, 22, 32, 
+	22, 22, 33, 34, 35, 36, 37, 22, 
+	39, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	26, 22, 23, 22, 25, 25, 22, 26, 
+	27, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 28, 29, 30, 31, 22, 
+	22, 22, 22, 22, 22, 22, 22, 45, 
+	22, 22, 22, 22, 22, 22, 37, 22, 
+	39, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	26, 22, 23, 22, 25, 25, 22, 26, 
+	27, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 28, 29, 30, 31, 22, 
+	22, 22, 22, 22, 22, 22, 22, 45, 
+	22, 22, 22, 22, 22, 22, 37, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	26, 22, 23, 22, 25, 25, 22, 26, 
+	27, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 28, 29, 30, 31, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 33, 22, 35, 22, 37, 22, 
+	39, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	26, 22, 23, 22, 25, 25, 22, 26, 
+	27, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 28, 29, 30, 31, 22, 
+	22, 22, 22, 22, 22, 22, 22, 45, 
+	22, 22, 33, 22, 22, 22, 37, 22, 
+	39, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	26, 22, 23, 22, 25, 25, 22, 26, 
+	27, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 28, 29, 30, 31, 22, 
+	22, 22, 22, 22, 22, 22, 22, 46, 
+	22, 22, 33, 34, 35, 22, 37, 22, 
+	39, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	26, 22, 23, 22, 25, 25, 22, 26, 
+	27, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 28, 29, 30, 31, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 33, 34, 35, 22, 37, 22, 
+	39, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	26, 22, 23, 24, 25, 25, 22, 26, 
+	27, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 28, 29, 30, 31, 22, 
+	22, 22, 22, 22, 22, 22, 22, 32, 
+	22, 22, 33, 34, 35, 36, 37, 22, 
+	39, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	26, 22, 48, 48, 47, 5, 47, 47, 
+	47, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 47, 12, 47, 47, 47, 
+	47, 47, 47, 47, 47, 49, 47, 47, 
+	47, 47, 47, 47, 18, 47, 47, 47, 
+	47, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 47, 47, 47, 5, 47, 
+	48, 48, 50, 5, 50, 50, 50, 50, 
+	50, 50, 50, 50, 50, 50, 50, 50, 
+	50, 50, 50, 50, 50, 50, 50, 50, 
+	50, 50, 50, 50, 50, 50, 50, 50, 
+	50, 50, 18, 50, 50, 50, 50, 50, 
+	50, 50, 50, 50, 50, 50, 50, 50, 
+	50, 50, 50, 50, 5, 50, 51, 47, 
+	48, 48, 47, 5, 18, 47, 47, 47, 
+	47, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 5, 47, 47, 47, 47, 
+	47, 47, 18, 47, 47, 47, 47, 47, 
+	47, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 47, 5, 47, 48, 48, 
 	47, 5, 47, 47, 47, 47, 47, 47, 
 	47, 5, 47, 47, 47, 47, 47, 47, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 47, 47, 47, 47, 47, 
+	47, 5, 47, 47, 47, 47, 47, 47, 
+	18, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 5, 47, 2, 47, 48, 48, 
+	47, 5, 6, 47, 47, 47, 47, 47, 
+	47, 47, 47, 47, 47, 52, 47, 47, 
+	12, 47, 47, 47, 47, 47, 47, 47, 
+	47, 53, 47, 47, 54, 47, 47, 47, 
+	18, 47, 53, 47, 47, 47, 47, 47, 
+	47, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 5, 47, 2, 47, 48, 48, 
+	47, 5, 6, 47, 47, 47, 47, 47, 
+	47, 47, 47, 47, 47, 47, 47, 47, 
+	12, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 47, 47, 47, 47, 47, 
+	18, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 5, 47, 2, 47, 48, 48, 
+	47, 5, 6, 47, 47, 47, 47, 47, 
+	47, 47, 47, 47, 47, 52, 47, 47, 
+	12, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 47, 47, 47, 47, 47, 
+	18, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 5, 47, 2, 47, 48, 48, 
+	47, 5, 6, 47, 47, 47, 47, 47, 
+	47, 47, 47, 47, 47, 52, 47, 47, 
+	12, 47, 47, 47, 47, 47, 47, 47, 
+	47, 53, 47, 47, 47, 47, 47, 47, 
+	18, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 5, 47, 2, 47, 48, 48, 
+	47, 5, 6, 47, 47, 47, 47, 47, 
+	47, 47, 47, 47, 47, 52, 47, 47, 
 	12, 47, 47, 47, 47, 47, 47, 47, 
 	12, 47, 47, 47, 47, 47, 47, 47, 
-	47, 49, 47, 47, 47, 47, 47, 47, 
-	18, 47, 48, 48, 47, 5, 47, 2, 
-	47, 48, 48, 47, 5, 6, 47, 47, 
+	47, 53, 47, 47, 47, 47, 47, 47, 
+	18, 47, 53, 47, 47, 47, 47, 47, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
-	50, 47, 47, 12, 47, 47, 47, 47, 
-	47, 47, 47, 47, 51, 47, 47, 52, 
-	47, 47, 47, 18, 47, 51, 47, 2, 
-	47, 48, 48, 47, 5, 6, 47, 47, 
+	47, 47, 5, 47, 48, 48, 47, 5, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
-	47, 47, 47, 12, 47, 47, 47, 47, 
+	47, 47, 47, 47, 47, 47, 12, 47, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
-	47, 47, 47, 18, 47, 53, 47, 48, 
-	48, 47, 5, 18, 47, 47, 47, 47, 
+	47, 47, 47, 47, 47, 47, 18, 47, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
-	47, 47, 54, 47, 47, 47, 47, 47, 
-	47, 18, 47, 48, 48, 47, 5, 47, 
+	5, 47, 55, 55, 47, 47, 47, 47, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
+	55, 47, 2, 3, 48, 48, 47, 5, 
+	6, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 9, 10, 11, 12, 47, 
+	47, 47, 47, 47, 47, 47, 47, 13, 
+	47, 47, 14, 15, 16, 17, 18, 19, 
+	20, 47, 47, 47, 47, 47, 47, 47, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
-	47, 47, 47, 47, 47, 47, 54, 47, 
-	47, 47, 47, 47, 47, 18, 47, 48, 
-	48, 47, 5, 47, 47, 47, 47, 47, 
+	5, 47, 2, 47, 48, 48, 47, 5, 
+	6, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 9, 10, 47, 12, 47, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 47, 47, 47, 18, 47, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
-	47, 18, 47, 2, 47, 48, 48, 47, 
-	5, 6, 47, 47, 47, 47, 47, 47, 
-	47, 47, 47, 47, 50, 47, 47, 12, 
+	5, 47, 2, 47, 48, 48, 47, 5, 
+	6, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 47, 10, 47, 12, 47, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
-	47, 47, 47, 47, 47, 47, 47, 18, 
-	47, 2, 47, 48, 48, 47, 5, 6, 
+	47, 47, 47, 47, 47, 47, 18, 47, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
-	47, 47, 50, 47, 47, 12, 47, 47, 
-	47, 47, 47, 47, 47, 47, 51, 47, 
-	47, 47, 47, 47, 47, 18, 47, 2, 
-	47, 48, 48, 47, 5, 6, 47, 47, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
-	50, 47, 47, 12, 47, 47, 47, 47, 
-	47, 47, 47, 47, 51, 47, 47, 47, 
-	47, 47, 47, 18, 47, 51, 47, 48, 
-	48, 47, 5, 47, 47, 47, 47, 47, 
+	5, 47, 2, 47, 48, 48, 47, 5, 
+	6, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 9, 10, 11, 12, 47, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
-	47, 12, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 47, 47, 47, 18, 56, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
-	47, 18, 47, 55, 55, 47, 47, 47, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
-	47, 55, 47, 2, 3, 48, 48, 47, 
-	5, 6, 47, 47, 47, 47, 47, 47, 
-	47, 47, 47, 47, 9, 10, 11, 12, 
+	5, 47, 2, 47, 48, 48, 47, 5, 
+	6, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 9, 10, 11, 12, 47, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
-	13, 47, 47, 14, 15, 16, 17, 18, 
-	19, 20, 47, 2, 47, 48, 48, 47, 
-	5, 6, 47, 47, 47, 47, 47, 47, 
-	47, 47, 47, 47, 9, 10, 47, 12, 
+	47, 47, 47, 47, 47, 47, 18, 47, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
-	47, 47, 47, 47, 47, 47, 47, 18, 
-	47, 2, 47, 48, 48, 47, 5, 6, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
-	47, 47, 47, 10, 47, 12, 47, 47, 
+	5, 47, 2, 47, 48, 48, 47, 5, 
+	6, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 9, 10, 11, 12, 47, 
+	47, 47, 47, 47, 47, 47, 47, 13, 
+	47, 47, 14, 15, 16, 17, 18, 47, 
+	20, 47, 47, 47, 47, 47, 47, 47, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
-	47, 47, 47, 47, 47, 18, 47, 2, 
-	47, 48, 48, 47, 5, 6, 47, 47, 
+	5, 47, 2, 47, 48, 48, 47, 5, 
+	6, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 9, 10, 11, 12, 47, 
+	47, 47, 47, 47, 47, 47, 47, 56, 
+	47, 47, 47, 47, 47, 47, 18, 47, 
+	20, 47, 47, 47, 47, 47, 47, 47, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
-	9, 10, 11, 12, 47, 47, 47, 47, 
+	5, 47, 2, 47, 48, 48, 47, 5, 
+	6, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 9, 10, 11, 12, 47, 
+	47, 47, 47, 47, 47, 47, 47, 56, 
+	47, 47, 47, 47, 47, 47, 18, 47, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
-	47, 47, 47, 18, 56, 47, 2, 47, 
-	48, 48, 47, 5, 6, 47, 47, 47, 
-	47, 47, 47, 47, 47, 47, 47, 9, 
-	10, 11, 12, 47, 47, 47, 47, 47, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
-	47, 47, 18, 47, 2, 47, 48, 48, 
-	47, 5, 6, 47, 47, 47, 47, 47, 
-	47, 47, 47, 47, 47, 9, 10, 11, 
-	12, 47, 47, 47, 47, 47, 47, 47, 
-	47, 13, 47, 47, 14, 15, 16, 17, 
-	18, 47, 20, 47, 2, 47, 48, 48, 
-	47, 5, 6, 47, 47, 47, 47, 47, 
-	47, 47, 47, 47, 47, 9, 10, 11, 
-	12, 47, 47, 47, 47, 47, 47, 47, 
-	47, 56, 47, 47, 47, 47, 47, 47, 
-	18, 47, 20, 47, 2, 47, 48, 48, 
-	47, 5, 6, 47, 47, 47, 47, 47, 
-	47, 47, 47, 47, 47, 9, 10, 11, 
-	12, 47, 47, 47, 47, 47, 47, 47, 
-	47, 56, 47, 47, 47, 47, 47, 47, 
-	18, 47, 2, 47, 48, 48, 47, 5, 
+	5, 47, 2, 47, 48, 48, 47, 5, 
 	6, 47, 47, 47, 47, 47, 47, 47, 
 	6, 47, 47, 47, 47, 47, 47, 47, 
 	47, 47, 47, 9, 10, 11, 12, 47, 
 	47, 47, 47, 9, 10, 11, 12, 47, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
 	47, 47, 14, 47, 16, 47, 18, 47, 
 	47, 47, 14, 47, 16, 47, 18, 47, 
-	20, 47, 2, 47, 48, 48, 47, 5, 
+	20, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 47, 47, 47, 47, 47, 
+	5, 47, 2, 47, 48, 48, 47, 5, 
 	6, 47, 47, 47, 47, 47, 47, 47, 
 	6, 47, 47, 47, 47, 47, 47, 47, 
 	47, 47, 47, 9, 10, 11, 12, 47, 
 	47, 47, 47, 9, 10, 11, 12, 47, 
 	47, 47, 47, 47, 47, 47, 47, 56, 
 	47, 47, 47, 47, 47, 47, 47, 56, 
 	47, 47, 14, 47, 47, 47, 18, 47, 
 	47, 47, 14, 47, 47, 47, 18, 47, 
-	20, 47, 2, 47, 48, 48, 47, 5, 
+	20, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 47, 47, 47, 47, 47, 
+	5, 47, 2, 47, 48, 48, 47, 5, 
 	6, 47, 47, 47, 47, 47, 47, 47, 
 	6, 47, 47, 47, 47, 47, 47, 47, 
 	47, 47, 47, 9, 10, 11, 12, 47, 
 	47, 47, 47, 9, 10, 11, 12, 47, 
 	47, 47, 47, 47, 47, 47, 47, 57, 
 	47, 47, 47, 47, 47, 47, 47, 57, 
 	47, 47, 14, 15, 16, 47, 18, 47, 
 	47, 47, 14, 15, 16, 47, 18, 47, 
-	20, 47, 2, 47, 48, 48, 47, 5, 
+	20, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 47, 47, 47, 47, 47, 
+	5, 47, 2, 47, 48, 48, 47, 5, 
 	6, 47, 47, 47, 47, 47, 47, 47, 
 	6, 47, 47, 47, 47, 47, 47, 47, 
 	47, 47, 47, 9, 10, 11, 12, 47, 
 	47, 47, 47, 9, 10, 11, 12, 47, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
 	47, 47, 47, 47, 47, 47, 47, 47, 
 	47, 47, 14, 15, 16, 47, 18, 47, 
 	47, 47, 14, 15, 16, 47, 18, 47, 
-	20, 47, 2, 3, 48, 48, 47, 5, 
+	20, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 47, 47, 47, 47, 47, 
+	5, 47, 2, 3, 48, 48, 47, 5, 
 	6, 47, 47, 47, 47, 47, 47, 47, 
 	6, 47, 47, 47, 47, 47, 47, 47, 
 	47, 47, 47, 9, 10, 11, 12, 47, 
 	47, 47, 47, 9, 10, 11, 12, 47, 
 	47, 47, 47, 47, 47, 47, 47, 13, 
 	47, 47, 47, 47, 47, 47, 47, 13, 
 	47, 47, 14, 15, 16, 17, 18, 47, 
 	47, 47, 14, 15, 16, 17, 18, 47, 
-	20, 47, 22, 23, 24, 24, 21, 25, 
-	26, 21, 21, 21, 21, 21, 21, 21, 
-	21, 21, 21, 27, 28, 29, 30, 21, 
-	21, 21, 21, 21, 21, 21, 21, 58, 
-	21, 21, 32, 33, 34, 35, 36, 37, 
-	38, 21, 22, 59, 24, 24, 21, 25, 
-	26, 21, 21, 21, 21, 21, 21, 21, 
-	21, 21, 21, 27, 28, 29, 30, 21, 
-	21, 21, 21, 21, 21, 21, 21, 31, 
-	21, 21, 32, 33, 34, 35, 36, 21, 
-	38, 21, 1, 1, 2, 3, 48, 48, 
+	20, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 47, 47, 47, 47, 47, 47, 
+	5, 47, 23, 24, 25, 25, 22, 26, 
+	27, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 28, 29, 30, 31, 22, 
+	22, 22, 22, 22, 22, 22, 22, 58, 
+	22, 22, 33, 34, 35, 36, 37, 38, 
+	39, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	26, 22, 23, 59, 25, 25, 22, 26, 
+	27, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 28, 29, 30, 31, 22, 
+	22, 22, 22, 22, 22, 22, 22, 32, 
+	22, 22, 33, 34, 35, 36, 37, 22, 
+	39, 22, 22, 22, 22, 22, 22, 22, 
+	22, 22, 22, 22, 22, 22, 22, 22, 
+	26, 22, 1, 1, 2, 3, 48, 48, 
 	47, 5, 6, 1, 1, 47, 47, 47, 
 	47, 5, 6, 1, 1, 47, 47, 47, 
 	1, 47, 47, 47, 47, 9, 10, 11, 
 	1, 47, 47, 47, 47, 9, 10, 11, 
 	12, 47, 47, 47, 47, 47, 47, 47, 
 	12, 47, 47, 47, 47, 47, 47, 47, 
 	47, 13, 47, 47, 14, 15, 16, 17, 
 	47, 13, 47, 47, 14, 15, 16, 17, 
-	18, 19, 20, 47, 1, 1, 60, 60, 
+	18, 19, 20, 47, 47, 47, 47, 47, 
+	47, 47, 47, 47, 47, 47, 47, 47, 
+	47, 47, 5, 47, 1, 1, 60, 60, 
 	60, 60, 60, 60, 60, 1, 1, 60, 
 	60, 60, 60, 60, 60, 1, 1, 60, 
 	60, 60, 1, 60, 0
 	60, 60, 1, 60, 0
 };
 };
 
 
 static const char _myanmar_syllable_machine_trans_targs[] = {
 static const char _myanmar_syllable_machine_trans_targs[] = {
-	0, 1, 26, 37, 0, 27, 29, 51, 
-	54, 39, 40, 41, 28, 43, 44, 46, 
-	47, 48, 30, 50, 45, 0, 2, 13, 
-	0, 3, 5, 14, 15, 16, 4, 18, 
-	19, 21, 22, 23, 6, 25, 20, 12, 
-	9, 10, 11, 7, 8, 17, 24, 0, 
-	0, 36, 33, 34, 35, 31, 32, 38, 
-	42, 49, 52, 53, 0
+	0, 1, 25, 35, 0, 26, 30, 49, 
+	52, 37, 38, 39, 29, 41, 42, 44, 
+	45, 46, 27, 48, 43, 26, 0, 2, 
+	12, 0, 3, 7, 13, 14, 15, 6, 
+	17, 18, 20, 21, 22, 4, 24, 19, 
+	11, 5, 8, 9, 10, 16, 23, 0, 
+	0, 34, 0, 28, 31, 32, 33, 36, 
+	40, 47, 50, 51, 0
 };
 };
 
 
 static const char _myanmar_syllable_machine_trans_actions[] = {
 static const char _myanmar_syllable_machine_trans_actions[] = {
-	3, 0, 0, 0, 4, 0, 0, 0, 
+	3, 0, 0, 0, 4, 5, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 5, 0, 0, 
-	6, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 6, 7, 0, 
+	0, 8, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 7, 
-	8, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 9
+	0, 0, 0, 0, 0, 0, 0, 9, 
+	10, 0, 11, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 12
 };
 };
 
 
 static const char _myanmar_syllable_machine_to_state_actions[] = {
 static const char _myanmar_syllable_machine_to_state_actions[] = {
@@ -390,7 +496,7 @@ static const char _myanmar_syllable_machine_to_state_actions[] = {
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0
+	0, 0, 0, 0, 0
 };
 };
 
 
 static const char _myanmar_syllable_machine_from_state_actions[] = {
 static const char _myanmar_syllable_machine_from_state_actions[] = {
@@ -400,17 +506,17 @@ static const char _myanmar_syllable_machine_from_state_actions[] = {
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0
+	0, 0, 0, 0, 0
 };
 };
 
 
 static const short _myanmar_syllable_machine_eof_trans[] = {
 static const short _myanmar_syllable_machine_eof_trans[] = {
-	0, 22, 22, 22, 22, 22, 22, 22, 
-	22, 22, 22, 22, 22, 22, 22, 22, 
-	22, 22, 22, 22, 22, 22, 22, 22, 
-	22, 22, 48, 48, 48, 48, 48, 48, 
+	0, 23, 23, 23, 23, 23, 23, 23, 
+	23, 23, 23, 23, 23, 23, 23, 23, 
+	23, 23, 23, 23, 23, 23, 23, 23, 
+	23, 48, 51, 48, 48, 48, 48, 48, 
 	48, 48, 48, 48, 48, 48, 48, 48, 
 	48, 48, 48, 48, 48, 48, 48, 48, 
 	48, 48, 48, 48, 48, 48, 48, 48, 
 	48, 48, 48, 48, 48, 48, 48, 48, 
-	48, 48, 48, 22, 22, 48, 61
+	48, 23, 23, 48, 61
 };
 };
 
 
 static const int myanmar_syllable_machine_start = 0;
 static const int myanmar_syllable_machine_start = 0;
@@ -424,7 +530,7 @@ static const int myanmar_syllable_machine_en_main = 0;
 
 
 
 
 
 
-#line 117 "hb-ot-shaper-myanmar-machine.rl"
+#line 118 "hb-ot-shaper-myanmar-machine.rl"
 
 
 
 
 #define found_syllable(syllable_type) \
 #define found_syllable(syllable_type) \
@@ -443,7 +549,7 @@ find_syllables_myanmar (hb_buffer_t *buffer)
   int cs;
   int cs;
   hb_glyph_info_t *info = buffer->info;
   hb_glyph_info_t *info = buffer->info;
   
   
-#line 447 "hb-ot-shaper-myanmar-machine.hh"
+#line 553 "hb-ot-shaper-myanmar-machine.hh"
 	{
 	{
 	cs = myanmar_syllable_machine_start;
 	cs = myanmar_syllable_machine_start;
 	ts = 0;
 	ts = 0;
@@ -451,7 +557,7 @@ find_syllables_myanmar (hb_buffer_t *buffer)
 	act = 0;
 	act = 0;
 	}
 	}
 
 
-#line 137 "hb-ot-shaper-myanmar-machine.rl"
+#line 138 "hb-ot-shaper-myanmar-machine.rl"
 
 
 
 
   p = 0;
   p = 0;
@@ -459,7 +565,7 @@ find_syllables_myanmar (hb_buffer_t *buffer)
 
 
   unsigned int syllable_serial = 1;
   unsigned int syllable_serial = 1;
   
   
-#line 463 "hb-ot-shaper-myanmar-machine.hh"
+#line 569 "hb-ot-shaper-myanmar-machine.hh"
 	{
 	{
 	int _slen;
 	int _slen;
 	int _trans;
 	int _trans;
@@ -473,7 +579,7 @@ _resume:
 #line 1 "NONE"
 #line 1 "NONE"
 	{ts = p;}
 	{ts = p;}
 	break;
 	break;
-#line 477 "hb-ot-shaper-myanmar-machine.hh"
+#line 583 "hb-ot-shaper-myanmar-machine.hh"
 	}
 	}
 
 
 	_keys = _myanmar_syllable_machine_trans_keys + (cs<<1);
 	_keys = _myanmar_syllable_machine_trans_keys + (cs<<1);
@@ -491,35 +597,59 @@ _eof_trans:
 		goto _again;
 		goto _again;
 
 
 	switch ( _myanmar_syllable_machine_trans_actions[_trans] ) {
 	switch ( _myanmar_syllable_machine_trans_actions[_trans] ) {
-	case 6:
-#line 110 "hb-ot-shaper-myanmar-machine.rl"
+	case 8:
+#line 111 "hb-ot-shaper-myanmar-machine.rl"
 	{te = p+1;{ found_syllable (myanmar_consonant_syllable); }}
 	{te = p+1;{ found_syllable (myanmar_consonant_syllable); }}
 	break;
 	break;
 	case 4:
 	case 4:
-#line 111 "hb-ot-shaper-myanmar-machine.rl"
+#line 112 "hb-ot-shaper-myanmar-machine.rl"
 	{te = p+1;{ found_syllable (myanmar_non_myanmar_cluster); }}
 	{te = p+1;{ found_syllable (myanmar_non_myanmar_cluster); }}
 	break;
 	break;
-	case 8:
-#line 112 "hb-ot-shaper-myanmar-machine.rl"
+	case 10:
+#line 113 "hb-ot-shaper-myanmar-machine.rl"
 	{te = p+1;{ found_syllable (myanmar_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; }}
 	{te = p+1;{ found_syllable (myanmar_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; }}
 	break;
 	break;
 	case 3:
 	case 3:
-#line 113 "hb-ot-shaper-myanmar-machine.rl"
+#line 114 "hb-ot-shaper-myanmar-machine.rl"
 	{te = p+1;{ found_syllable (myanmar_non_myanmar_cluster); }}
 	{te = p+1;{ found_syllable (myanmar_non_myanmar_cluster); }}
 	break;
 	break;
-	case 5:
-#line 110 "hb-ot-shaper-myanmar-machine.rl"
-	{te = p;p--;{ found_syllable (myanmar_consonant_syllable); }}
-	break;
 	case 7:
 	case 7:
-#line 112 "hb-ot-shaper-myanmar-machine.rl"
-	{te = p;p--;{ found_syllable (myanmar_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; }}
+#line 111 "hb-ot-shaper-myanmar-machine.rl"
+	{te = p;p--;{ found_syllable (myanmar_consonant_syllable); }}
 	break;
 	break;
 	case 9:
 	case 9:
 #line 113 "hb-ot-shaper-myanmar-machine.rl"
 #line 113 "hb-ot-shaper-myanmar-machine.rl"
+	{te = p;p--;{ found_syllable (myanmar_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; }}
+	break;
+	case 12:
+#line 114 "hb-ot-shaper-myanmar-machine.rl"
 	{te = p;p--;{ found_syllable (myanmar_non_myanmar_cluster); }}
 	{te = p;p--;{ found_syllable (myanmar_non_myanmar_cluster); }}
 	break;
 	break;
-#line 523 "hb-ot-shaper-myanmar-machine.hh"
+	case 11:
+#line 1 "NONE"
+	{	switch( act ) {
+	case 2:
+	{{p = ((te))-1;} found_syllable (myanmar_non_myanmar_cluster); }
+	break;
+	case 3:
+	{{p = ((te))-1;} found_syllable (myanmar_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; }
+	break;
+	}
+	}
+	break;
+	case 6:
+#line 1 "NONE"
+	{te = p+1;}
+#line 112 "hb-ot-shaper-myanmar-machine.rl"
+	{act = 2;}
+	break;
+	case 5:
+#line 1 "NONE"
+	{te = p+1;}
+#line 113 "hb-ot-shaper-myanmar-machine.rl"
+	{act = 3;}
+	break;
+#line 653 "hb-ot-shaper-myanmar-machine.hh"
 	}
 	}
 
 
 _again:
 _again:
@@ -528,7 +658,7 @@ _again:
 #line 1 "NONE"
 #line 1 "NONE"
 	{ts = 0;}
 	{ts = 0;}
 	break;
 	break;
-#line 532 "hb-ot-shaper-myanmar-machine.hh"
+#line 662 "hb-ot-shaper-myanmar-machine.hh"
 	}
 	}
 
 
 	if ( ++p != pe )
 	if ( ++p != pe )
@@ -544,7 +674,7 @@ _again:
 
 
 	}
 	}
 
 
-#line 145 "hb-ot-shaper-myanmar-machine.rl"
+#line 146 "hb-ot-shaper-myanmar-machine.rl"
 
 
 }
 }
 
 

文件差异内容过多而无法显示
+ 550 - 550
thirdparty/harfbuzz/src/hb-ot-shaper-use-machine.hh


+ 1 - 1
thirdparty/harfbuzz/src/hb-ot-shaper-vowel-constraints.cc

@@ -24,7 +24,7 @@ static void
 _output_dotted_circle (hb_buffer_t *buffer)
 _output_dotted_circle (hb_buffer_t *buffer)
 {
 {
   (void) buffer->output_glyph (0x25CCu);
   (void) buffer->output_glyph (0x25CCu);
-  _hb_glyph_info_reset_continuation (&buffer->prev());
+  _hb_glyph_info_clear_continuation (&buffer->prev());
 }
 }
 
 
 static void
 static void

+ 210 - 3
thirdparty/harfbuzz/src/hb-ot-tag-table.hh

@@ -6,8 +6,8 @@
  *
  *
  * on files with these headers:
  * on files with these headers:
  *
  *
- * <meta name="updated_at" content="2024-07-07 12:57 AM" />
- * File-Date: 2024-06-14
+ * <meta name="updated_at" content="2024-12-06 06:35 AM" />
+ * File-Date: 2025-01-21
  */
  */
 
 
 #ifndef HB_OT_TAG_TABLE_HH
 #ifndef HB_OT_TAG_TABLE_HH
@@ -745,6 +745,7 @@ static const LangTag ot_languages3[] = {
 /*{HB_TAG('h','n','d',' '),	HB_TAG('H','N','D',' ')},*/	/* Southern Hindko -> Hindko */
 /*{HB_TAG('h','n','d',' '),	HB_TAG('H','N','D',' ')},*/	/* Southern Hindko -> Hindko */
   {HB_TAG('h','n','e',' '),	HB_TAG('C','H','H',' ')},	/* Chhattisgarhi -> Chattisgarhi */
   {HB_TAG('h','n','e',' '),	HB_TAG('C','H','H',' ')},	/* Chhattisgarhi -> Chattisgarhi */
   {HB_TAG('h','n','j',' '),	HB_TAG('H','M','N',' ')},	/* Hmong Njua -> Hmong */
   {HB_TAG('h','n','j',' '),	HB_TAG('H','M','N',' ')},	/* Hmong Njua -> Hmong */
+  {HB_TAG('h','n','m',' '),	HB_TAG('Z','H','S',' ')},	/* Hainanese -> Chinese, Simplified */
   {HB_TAG('h','n','o',' '),	HB_TAG('H','N','D',' ')},	/* Northern Hindko -> Hindko */
   {HB_TAG('h','n','o',' '),	HB_TAG('H','N','D',' ')},	/* Northern Hindko -> Hindko */
   {HB_TAG('h','o','c',' '),	HB_TAG('H','O',' ',' ')},	/* Ho */
   {HB_TAG('h','o','c',' '),	HB_TAG('H','O',' ',' ')},	/* Ho */
   {HB_TAG('h','o','i',' '),	HB_TAG('A','T','H',' ')},	/* Holikachuk -> Athapaskan */
   {HB_TAG('h','o','i',' '),	HB_TAG('A','T','H',' ')},	/* Holikachuk -> Athapaskan */
@@ -830,6 +831,7 @@ static const LangTag ot_languages3[] = {
   {HB_TAG('k','f','x',' '),	HB_TAG('K','U','L',' ')},	/* Kullu Pahari -> Kulvi */
   {HB_TAG('k','f','x',' '),	HB_TAG('K','U','L',' ')},	/* Kullu Pahari -> Kulvi */
   {HB_TAG('k','f','y',' '),	HB_TAG('K','M','N',' ')},	/* Kumaoni */
   {HB_TAG('k','f','y',' '),	HB_TAG('K','M','N',' ')},	/* Kumaoni */
   {HB_TAG('k','g','e',' '),	HB_TAG_NONE	       },	/* Komering != Khutsuri Georgian */
   {HB_TAG('k','g','e',' '),	HB_TAG_NONE	       },	/* Komering != Khutsuri Georgian */
+/*{HB_TAG('k','g','f',' '),	HB_TAG('K','G','F',' ')},*/	/* Kube */
   {HB_TAG('k','h','a',' '),	HB_TAG('K','S','I',' ')},	/* Khasi */
   {HB_TAG('k','h','a',' '),	HB_TAG('K','S','I',' ')},	/* Khasi */
   {HB_TAG('k','h','b',' '),	HB_TAG('X','B','D',' ')},	/* Lü */
   {HB_TAG('k','h','b',' '),	HB_TAG('X','B','D',' ')},	/* Lü */
   {HB_TAG('k','h','k',' '),	HB_TAG('M','N','G',' ')},	/* Halh Mongolian -> Mongolian */
   {HB_TAG('k','h','k',' '),	HB_TAG('M','N','G',' ')},	/* Halh Mongolian -> Mongolian */
@@ -855,6 +857,7 @@ static const LangTag ot_languages3[] = {
   {HB_TAG('k','l','m',' '),	HB_TAG_NONE	       },	/* Migum != Kalmyk */
   {HB_TAG('k','l','m',' '),	HB_TAG_NONE	       },	/* Migum != Kalmyk */
   {HB_TAG('k','l','n',' '),	HB_TAG('K','A','L',' ')},	/* Kalenjin [macrolanguage] */
   {HB_TAG('k','l','n',' '),	HB_TAG('K','A','L',' ')},	/* Kalenjin [macrolanguage] */
   {HB_TAG('k','m','b',' '),	HB_TAG('M','B','N',' ')},	/* Kimbundu -> Mbundu */
   {HB_TAG('k','m','b',' '),	HB_TAG('M','B','N',' ')},	/* Kimbundu -> Mbundu */
+/*{HB_TAG('k','m','g',' '),	HB_TAG('K','M','G',' ')},*/	/* Kâte */
   {HB_TAG('k','m','n',' '),	HB_TAG_NONE	       },	/* Awtuw != Kumaoni */
   {HB_TAG('k','m','n',' '),	HB_TAG_NONE	       },	/* Awtuw != Kumaoni */
   {HB_TAG('k','m','o',' '),	HB_TAG_NONE	       },	/* Kwoma != Komo */
   {HB_TAG('k','m','o',' '),	HB_TAG_NONE	       },	/* Kwoma != Komo */
   {HB_TAG('k','m','r',' '),	HB_TAG('K','U','R',' ')},	/* Northern Kurdish -> Kurdish */
   {HB_TAG('k','m','r',' '),	HB_TAG('K','U','R',' ')},	/* Northern Kurdish -> Kurdish */
@@ -898,6 +901,7 @@ static const LangTag ot_languages3[] = {
   {HB_TAG('k','s','i',' '),	HB_TAG_NONE	       },	/* Krisa != Khasi */
   {HB_TAG('k','s','i',' '),	HB_TAG_NONE	       },	/* Krisa != Khasi */
   {HB_TAG('k','s','m',' '),	HB_TAG_NONE	       },	/* Kumba != Kildin Sami */
   {HB_TAG('k','s','m',' '),	HB_TAG_NONE	       },	/* Kumba != Kildin Sami */
   {HB_TAG('k','s','s',' '),	HB_TAG('K','I','S',' ')},	/* Southern Kisi -> Kisii */
   {HB_TAG('k','s','s',' '),	HB_TAG('K','I','S',' ')},	/* Southern Kisi -> Kisii */
+/*{HB_TAG('k','s','u',' '),	HB_TAG('K','S','U',' ')},*/	/* Khamyang */
   {HB_TAG('k','s','w',' '),	HB_TAG('K','S','W',' ')},	/* S’gaw Karen */
   {HB_TAG('k','s','w',' '),	HB_TAG('K','S','W',' ')},	/* S’gaw Karen */
   {HB_TAG('k','s','w',' '),	HB_TAG('K','R','N',' ')},	/* S'gaw Karen -> Karen */
   {HB_TAG('k','s','w',' '),	HB_TAG('K','R','N',' ')},	/* S'gaw Karen -> Karen */
   {HB_TAG('k','t','b',' '),	HB_TAG('K','E','B',' ')},	/* Kambaata -> Kebena */
   {HB_TAG('k','t','b',' '),	HB_TAG('K','E','B',' ')},	/* Kambaata -> Kebena */
@@ -911,6 +915,7 @@ static const LangTag ot_languages3[] = {
   {HB_TAG('k','u','y',' '),	HB_TAG_NONE	       },	/* Kuuku-Ya'u != Kuy */
   {HB_TAG('k','u','y',' '),	HB_TAG_NONE	       },	/* Kuuku-Ya'u != Kuy */
   {HB_TAG('k','v','b',' '),	HB_TAG('M','L','Y',' ')},	/* Kubu -> Malay */
   {HB_TAG('k','v','b',' '),	HB_TAG('M','L','Y',' ')},	/* Kubu -> Malay */
   {HB_TAG('k','v','l',' '),	HB_TAG('K','R','N',' ')},	/* Kayaw -> Karen */
   {HB_TAG('k','v','l',' '),	HB_TAG('K','R','N',' ')},	/* Kayaw -> Karen */
+  {HB_TAG('k','v','q',' '),	HB_TAG('K','V','Q',' ')},	/* Geba Karen */
   {HB_TAG('k','v','q',' '),	HB_TAG('K','R','N',' ')},	/* Geba Karen -> Karen */
   {HB_TAG('k','v','q',' '),	HB_TAG('K','R','N',' ')},	/* Geba Karen -> Karen */
   {HB_TAG('k','v','r',' '),	HB_TAG('M','L','Y',' ')},	/* Kerinci -> Malay */
   {HB_TAG('k','v','r',' '),	HB_TAG('M','L','Y',' ')},	/* Kerinci -> Malay */
   {HB_TAG('k','v','t',' '),	HB_TAG('K','R','N',' ')},	/* Lahta Karen -> Karen */
   {HB_TAG('k','v','t',' '),	HB_TAG('K','R','N',' ')},	/* Lahta Karen -> Karen */
@@ -977,9 +982,11 @@ static const LangTag ot_languages3[] = {
   {HB_TAG('l','t','o',' '),	HB_TAG('L','U','H',' ')},	/* Tsotso -> Luyia */
   {HB_TAG('l','t','o',' '),	HB_TAG('L','U','H',' ')},	/* Tsotso -> Luyia */
   {HB_TAG('l','t','s',' '),	HB_TAG('L','U','H',' ')},	/* Tachoni -> Luyia */
   {HB_TAG('l','t','s',' '),	HB_TAG('L','U','H',' ')},	/* Tachoni -> Luyia */
 /*{HB_TAG('l','u','a',' '),	HB_TAG('L','U','A',' ')},*/	/* Luba-Lulua */
 /*{HB_TAG('l','u','a',' '),	HB_TAG('L','U','A',' ')},*/	/* Luba-Lulua */
+  {HB_TAG('l','u','h',' '),	HB_TAG('Z','H','S',' ')},	/* Leizhou Chinese -> Chinese, Simplified */
 /*{HB_TAG('l','u','o',' '),	HB_TAG('L','U','O',' ')},*/	/* Luo (Kenya and Tanzania) */
 /*{HB_TAG('l','u','o',' '),	HB_TAG('L','U','O',' ')},*/	/* Luo (Kenya and Tanzania) */
   {HB_TAG('l','u','s',' '),	HB_TAG('M','I','Z',' ')},	/* Lushai -> Mizo */
   {HB_TAG('l','u','s',' '),	HB_TAG('M','I','Z',' ')},	/* Lushai -> Mizo */
   {HB_TAG('l','u','s',' '),	HB_TAG('Q','I','N',' ')},	/* Lushai -> Chin */
   {HB_TAG('l','u','s',' '),	HB_TAG('Q','I','N',' ')},	/* Lushai -> Chin */
+/*{HB_TAG('l','u','t',' '),	HB_TAG('L','U','T',' ')},*/	/* Lushootseed */
   {HB_TAG('l','u','y',' '),	HB_TAG('L','U','H',' ')},	/* Luyia [macrolanguage] */
   {HB_TAG('l','u','y',' '),	HB_TAG('L','U','H',' ')},	/* Luyia [macrolanguage] */
   {HB_TAG('l','u','z',' '),	HB_TAG('L','R','C',' ')},	/* Southern Luri -> Luri */
   {HB_TAG('l','u','z',' '),	HB_TAG('L','R','C',' ')},	/* Southern Luri -> Luri */
   {HB_TAG('l','v','i',' '),	HB_TAG_NONE	       },	/* Lavi != Latvian */
   {HB_TAG('l','v','i',' '),	HB_TAG_NONE	       },	/* Lavi != Latvian */
@@ -1146,6 +1153,7 @@ static const LangTag ot_languages3[] = {
   {HB_TAG('n','o','d',' '),	HB_TAG('N','T','A',' ')},	/* Northern Thai -> Northern Tai */
   {HB_TAG('n','o','d',' '),	HB_TAG('N','T','A',' ')},	/* Northern Thai -> Northern Tai */
 /*{HB_TAG('n','o','e',' '),	HB_TAG('N','O','E',' ')},*/	/* Nimadi */
 /*{HB_TAG('n','o','e',' '),	HB_TAG('N','O','E',' ')},*/	/* Nimadi */
 /*{HB_TAG('n','o','g',' '),	HB_TAG('N','O','G',' ')},*/	/* Nogai */
 /*{HB_TAG('n','o','g',' '),	HB_TAG('N','O','G',' ')},*/	/* Nogai */
+/*{HB_TAG('n','o','p',' '),	HB_TAG('N','O','P',' ')},*/	/* Numanggang */
 /*{HB_TAG('n','o','v',' '),	HB_TAG('N','O','V',' ')},*/	/* Novial */
 /*{HB_TAG('n','o','v',' '),	HB_TAG('N','O','V',' ')},*/	/* Novial */
   {HB_TAG('n','p','i',' '),	HB_TAG('N','E','P',' ')},	/* Nepali */
   {HB_TAG('n','p','i',' '),	HB_TAG('N','E','P',' ')},	/* Nepali */
   {HB_TAG('n','p','l',' '),	HB_TAG('N','A','H',' ')},	/* Southeastern Puebla Nahuatl -> Nahuatl */
   {HB_TAG('n','p','l',' '),	HB_TAG('N','A','H',' ')},	/* Southeastern Puebla Nahuatl -> Nahuatl */
@@ -1156,6 +1164,7 @@ static const LangTag ot_languages3[] = {
   {HB_TAG('n','s','u',' '),	HB_TAG('N','A','H',' ')},	/* Sierra Negra Nahuatl -> Nahuatl */
   {HB_TAG('n','s','u',' '),	HB_TAG('N','A','H',' ')},	/* Sierra Negra Nahuatl -> Nahuatl */
   {HB_TAG('n','t','o',' '),	HB_TAG_NONE	       },	/* Ntomba != Esperanto */
   {HB_TAG('n','t','o',' '),	HB_TAG_NONE	       },	/* Ntomba != Esperanto */
   {HB_TAG('n','u','e',' '),	HB_TAG('B','A','D','0')},	/* Ngundu -> Banda */
   {HB_TAG('n','u','e',' '),	HB_TAG('B','A','D','0')},	/* Ngundu -> Banda */
+/*{HB_TAG('n','u','k',' '),	HB_TAG('N','U','K',' ')},*/	/* Nuu-chah-nulth */
   {HB_TAG('n','u','u',' '),	HB_TAG('B','A','D','0')},	/* Ngbundu -> Banda */
   {HB_TAG('n','u','u',' '),	HB_TAG('B','A','D','0')},	/* Ngbundu -> Banda */
   {HB_TAG('n','u','z',' '),	HB_TAG('N','A','H',' ')},	/* Tlamacazapa Nahuatl -> Nahuatl */
   {HB_TAG('n','u','z',' '),	HB_TAG('N','A','H',' ')},	/* Tlamacazapa Nahuatl -> Nahuatl */
   {HB_TAG('n','w','e',' '),	HB_TAG('B','M','L',' ')},	/* Ngwe -> Bamileke */
   {HB_TAG('n','w','e',' '),	HB_TAG('B','M','L',' ')},	/* Ngwe -> Bamileke */
@@ -1398,9 +1407,12 @@ static const LangTag ot_languages3[] = {
   {HB_TAG('s','i','g',' '),	HB_TAG_NONE	       },	/* Paasaal != Silte Gurage */
   {HB_TAG('s','i','g',' '),	HB_TAG_NONE	       },	/* Paasaal != Silte Gurage */
   {HB_TAG('s','i','z',' '),	HB_TAG('B','B','R',' ')},	/* Siwi -> Berber */
   {HB_TAG('s','i','z',' '),	HB_TAG('B','B','R',' ')},	/* Siwi -> Berber */
 /*{HB_TAG('s','j','a',' '),	HB_TAG('S','J','A',' ')},*/	/* Epena */
 /*{HB_TAG('s','j','a',' '),	HB_TAG('S','J','A',' ')},*/	/* Epena */
+  {HB_TAG('s','j','c',' '),	HB_TAG('Z','H','S',' ')},	/* Shaojiang Chinese -> Chinese, Simplified */
   {HB_TAG('s','j','d',' '),	HB_TAG('K','S','M',' ')},	/* Kildin Sami */
   {HB_TAG('s','j','d',' '),	HB_TAG('K','S','M',' ')},	/* Kildin Sami */
+/*{HB_TAG('s','j','e',' '),	HB_TAG('S','J','E',' ')},*/	/* Pite Sami */
   {HB_TAG('s','j','o',' '),	HB_TAG('S','I','B',' ')},	/* Xibe -> Sibe */
   {HB_TAG('s','j','o',' '),	HB_TAG('S','I','B',' ')},	/* Xibe -> Sibe */
   {HB_TAG('s','j','s',' '),	HB_TAG('B','B','R',' ')},	/* Senhaja De Srair -> Berber */
   {HB_TAG('s','j','s',' '),	HB_TAG('B','B','R',' ')},	/* Senhaja De Srair -> Berber */
+/*{HB_TAG('s','j','u',' '),	HB_TAG('S','J','U',' ')},*/	/* Ume Sami */
   {HB_TAG('s','k','g',' '),	HB_TAG('M','L','G',' ')},	/* Sakalava Malagasy -> Malagasy */
   {HB_TAG('s','k','g',' '),	HB_TAG('M','L','G',' ')},	/* Sakalava Malagasy -> Malagasy */
   {HB_TAG('s','k','r',' '),	HB_TAG('S','R','K',' ')},	/* Saraiki */
   {HB_TAG('s','k','r',' '),	HB_TAG('S','R','K',' ')},	/* Saraiki */
   {HB_TAG('s','k','s',' '),	HB_TAG_NONE	       },	/* Maia != Skolt Sami */
   {HB_TAG('s','k','s',' '),	HB_TAG_NONE	       },	/* Maia != Skolt Sami */
@@ -1461,6 +1473,7 @@ static const LangTag ot_languages3[] = {
   {HB_TAG('t','a','q',' '),	HB_TAG('B','B','R',' ')},	/* Tamasheq -> Berber */
   {HB_TAG('t','a','q',' '),	HB_TAG('B','B','R',' ')},	/* Tamasheq -> Berber */
   {HB_TAG('t','a','s',' '),	HB_TAG('C','P','P',' ')},	/* Tay Boi -> Creoles */
   {HB_TAG('t','a','s',' '),	HB_TAG('C','P','P',' ')},	/* Tay Boi -> Creoles */
   {HB_TAG('t','a','u',' '),	HB_TAG('A','T','H',' ')},	/* Upper Tanana -> Athapaskan */
   {HB_TAG('t','a','u',' '),	HB_TAG('A','T','H',' ')},	/* Upper Tanana -> Athapaskan */
+/*{HB_TAG('t','b','v',' '),	HB_TAG('T','B','V',' ')},*/	/* Tobo */
   {HB_TAG('t','c','b',' '),	HB_TAG('A','T','H',' ')},	/* Tanacross -> Athapaskan */
   {HB_TAG('t','c','b',' '),	HB_TAG('A','T','H',' ')},	/* Tanacross -> Athapaskan */
   {HB_TAG('t','c','e',' '),	HB_TAG('A','T','H',' ')},	/* Southern Tutchone -> Athapaskan */
   {HB_TAG('t','c','e',' '),	HB_TAG('A','T','H',' ')},	/* Southern Tutchone -> Athapaskan */
   {HB_TAG('t','c','h',' '),	HB_TAG('C','P','P',' ')},	/* Turks And Caicos Creole English -> Creoles */
   {HB_TAG('t','c','h',' '),	HB_TAG('C','P','P',' ')},	/* Turks And Caicos Creole English -> Creoles */
@@ -1623,7 +1636,7 @@ static const LangTag ot_languages3[] = {
   {HB_TAG('y','b','a',' '),	HB_TAG_NONE	       },	/* Yala != Yoruba */
   {HB_TAG('y','b','a',' '),	HB_TAG_NONE	       },	/* Yala != Yoruba */
   {HB_TAG('y','b','b',' '),	HB_TAG('B','M','L',' ')},	/* Yemba -> Bamileke */
   {HB_TAG('y','b','b',' '),	HB_TAG('B','M','L',' ')},	/* Yemba -> Bamileke */
   {HB_TAG('y','b','d',' '),	HB_TAG('A','R','K',' ')},	/* Yangbye (retired code) -> Rakhine */
   {HB_TAG('y','b','d',' '),	HB_TAG('A','R','K',' ')},	/* Yangbye (retired code) -> Rakhine */
-  {HB_TAG('y','c','r',' '),	HB_TAG_NONE	       },	/* Yilan Creole != Y-Cree */
+  {HB_TAG('y','c','r',' '),	HB_TAG('C','P','P',' ')},	/* Yilan Creole -> Creoles */
   {HB_TAG('y','d','d',' '),	HB_TAG('J','I','I',' ')},	/* Eastern Yiddish -> Yiddish */
   {HB_TAG('y','d','d',' '),	HB_TAG('J','I','I',' ')},	/* Eastern Yiddish -> Yiddish */
 /*{HB_TAG('y','g','p',' '),	HB_TAG('Y','G','P',' ')},*/	/* Gepo */
 /*{HB_TAG('y','g','p',' '),	HB_TAG('Y','G','P',' ')},*/	/* Gepo */
   {HB_TAG('y','i','h',' '),	HB_TAG('J','I','I',' ')},	/* Western Yiddish -> Yiddish */
   {HB_TAG('y','i','h',' '),	HB_TAG('J','I','I',' ')},	/* Western Yiddish -> Yiddish */
@@ -2377,6 +2390,26 @@ out:
       *count = i;
       *count = i;
       return true;
       return true;
     }
     }
+    if (lang_matches (&lang_str[1], limit, "nm-hant-hk", 10))
+    {
+      /* Hainanese; Han (Traditional variant); Hong Kong */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], limit, "nm-hant-mo", 10))
+    {
+      /* Hainanese; Han (Traditional variant); Macao */
+      unsigned int i;
+      hb_tag_t possible_tags[] = {
+	HB_TAG('Z','H','T','M'),  /* Chinese, Traditional, Macao SAR */
+	HB_TAG('Z','H','H',' '),  /* Chinese, Traditional, Hong Kong SAR */
+      };
+      for (i = 0; i < 2 && i < *count; i++)
+	tags[i] = possible_tags[i];
+      *count = i;
+      return true;
+    }
     if (lang_matches (&lang_str[1], limit, "sn-hant-hk", 10))
     if (lang_matches (&lang_str[1], limit, "sn-hant-hk", 10))
     {
     {
       /* Xiang Chinese; Han (Traditional variant); Hong Kong */
       /* Xiang Chinese; Han (Traditional variant); Hong Kong */
@@ -2411,6 +2444,20 @@ out:
       *count = 1;
       *count = 1;
       return true;
       return true;
     }
     }
+    if (lang_matches (&lang_str[1], limit, "nm-hans", 7))
+    {
+      /* Hainanese; Han (Simplified variant) */
+      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese, Simplified */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], limit, "nm-hant", 7))
+    {
+      /* Hainanese; Han (Traditional variant) */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
+      *count = 1;
+      return true;
+    }
     if (lang_matches (&lang_str[1], limit, "sn-hans", 7))
     if (lang_matches (&lang_str[1], limit, "sn-hans", 7))
     {
     {
       /* Xiang Chinese; Han (Simplified variant) */
       /* Xiang Chinese; Han (Simplified variant) */
@@ -2455,6 +2502,36 @@ out:
       *count = 1;
       *count = 1;
       return true;
       return true;
     }
     }
+    if (0 == strncmp (&lang_str[1], "nm-", 3)
+	&& subtag_matches (lang_str, limit, "-hk", 3))
+    {
+      /* Hainanese; Hong Kong */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "nm-", 3)
+	&& subtag_matches (lang_str, limit, "-mo", 3))
+    {
+      /* Hainanese; Macao */
+      unsigned int i;
+      hb_tag_t possible_tags[] = {
+	HB_TAG('Z','H','T','M'),  /* Chinese, Traditional, Macao SAR */
+	HB_TAG('Z','H','H',' '),  /* Chinese, Traditional, Hong Kong SAR */
+      };
+      for (i = 0; i < 2 && i < *count; i++)
+	tags[i] = possible_tags[i];
+      *count = i;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "nm-", 3)
+	&& subtag_matches (lang_str, limit, "-tw", 3))
+    {
+      /* Hainanese; Taiwan, Province of China */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
+      *count = 1;
+      return true;
+    }
     if (0 == strncmp (&lang_str[1], "sn-", 3)
     if (0 == strncmp (&lang_str[1], "sn-", 3)
 	&& subtag_matches (lang_str, limit, "-hk", 3))
 	&& subtag_matches (lang_str, limit, "-hk", 3))
     {
     {
@@ -2516,6 +2593,40 @@ out:
     }
     }
     break;
     break;
   case 'l':
   case 'l':
+    if (lang_matches (&lang_str[1], limit, "uh-hant-hk", 10))
+    {
+      /* Leizhou Chinese; Han (Traditional variant); Hong Kong */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], limit, "uh-hant-mo", 10))
+    {
+      /* Leizhou Chinese; Han (Traditional variant); Macao */
+      unsigned int i;
+      hb_tag_t possible_tags[] = {
+	HB_TAG('Z','H','T','M'),  /* Chinese, Traditional, Macao SAR */
+	HB_TAG('Z','H','H',' '),  /* Chinese, Traditional, Hong Kong SAR */
+      };
+      for (i = 0; i < 2 && i < *count; i++)
+	tags[i] = possible_tags[i];
+      *count = i;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], limit, "uh-hans", 7))
+    {
+      /* Leizhou Chinese; Han (Simplified variant) */
+      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese, Simplified */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], limit, "uh-hant", 7))
+    {
+      /* Leizhou Chinese; Han (Traditional variant) */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
+      *count = 1;
+      return true;
+    }
     if (lang_matches (&lang_str[1], limit, "zh-hans", 7))
     if (lang_matches (&lang_str[1], limit, "zh-hans", 7))
     {
     {
       /* Literary Chinese; Han (Simplified variant) */
       /* Literary Chinese; Han (Simplified variant) */
@@ -2523,6 +2634,36 @@ out:
       *count = 1;
       *count = 1;
       return true;
       return true;
     }
     }
+    if (0 == strncmp (&lang_str[1], "uh-", 3)
+	&& subtag_matches (lang_str, limit, "-hk", 3))
+    {
+      /* Leizhou Chinese; Hong Kong */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "uh-", 3)
+	&& subtag_matches (lang_str, limit, "-mo", 3))
+    {
+      /* Leizhou Chinese; Macao */
+      unsigned int i;
+      hb_tag_t possible_tags[] = {
+	HB_TAG('Z','H','T','M'),  /* Chinese, Traditional, Macao SAR */
+	HB_TAG('Z','H','H',' '),  /* Chinese, Traditional, Hong Kong SAR */
+      };
+      for (i = 0; i < 2 && i < *count; i++)
+	tags[i] = possible_tags[i];
+      *count = i;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "uh-", 3)
+	&& subtag_matches (lang_str, limit, "-tw", 3))
+    {
+      /* Leizhou Chinese; Taiwan, Province of China */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
+      *count = 1;
+      return true;
+    }
     break;
     break;
   case 'm':
   case 'm':
     if (lang_matches (&lang_str[1], limit, "np-hant-hk", 10))
     if (lang_matches (&lang_str[1], limit, "np-hant-hk", 10))
@@ -2694,6 +2835,72 @@ out:
       return true;
       return true;
     }
     }
     break;
     break;
+  case 's':
+    if (lang_matches (&lang_str[1], limit, "jc-hant-hk", 10))
+    {
+      /* Shaojiang Chinese; Han (Traditional variant); Hong Kong */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], limit, "jc-hant-mo", 10))
+    {
+      /* Shaojiang Chinese; Han (Traditional variant); Macao */
+      unsigned int i;
+      hb_tag_t possible_tags[] = {
+	HB_TAG('Z','H','T','M'),  /* Chinese, Traditional, Macao SAR */
+	HB_TAG('Z','H','H',' '),  /* Chinese, Traditional, Hong Kong SAR */
+      };
+      for (i = 0; i < 2 && i < *count; i++)
+	tags[i] = possible_tags[i];
+      *count = i;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], limit, "jc-hans", 7))
+    {
+      /* Shaojiang Chinese; Han (Simplified variant) */
+      tags[0] = HB_TAG('Z','H','S',' ');  /* Chinese, Simplified */
+      *count = 1;
+      return true;
+    }
+    if (lang_matches (&lang_str[1], limit, "jc-hant", 7))
+    {
+      /* Shaojiang Chinese; Han (Traditional variant) */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
+      *count = 1;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "jc-", 3)
+	&& subtag_matches (lang_str, limit, "-hk", 3))
+    {
+      /* Shaojiang Chinese; Hong Kong */
+      tags[0] = HB_TAG('Z','H','H',' ');  /* Chinese, Traditional, Hong Kong SAR */
+      *count = 1;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "jc-", 3)
+	&& subtag_matches (lang_str, limit, "-mo", 3))
+    {
+      /* Shaojiang Chinese; Macao */
+      unsigned int i;
+      hb_tag_t possible_tags[] = {
+	HB_TAG('Z','H','T','M'),  /* Chinese, Traditional, Macao SAR */
+	HB_TAG('Z','H','H',' '),  /* Chinese, Traditional, Hong Kong SAR */
+      };
+      for (i = 0; i < 2 && i < *count; i++)
+	tags[i] = possible_tags[i];
+      *count = i;
+      return true;
+    }
+    if (0 == strncmp (&lang_str[1], "jc-", 3)
+	&& subtag_matches (lang_str, limit, "-tw", 3))
+    {
+      /* Shaojiang Chinese; Taiwan, Province of China */
+      tags[0] = HB_TAG('Z','H','T',' ');  /* Chinese, Traditional */
+      *count = 1;
+      return true;
+    }
+    break;
   case 'w':
   case 'w':
     if (lang_matches (&lang_str[1], limit, "uu-hant-hk", 10))
     if (lang_matches (&lang_str[1], limit, "uu-hant-hk", 10))
     {
     {

+ 41 - 23
thirdparty/harfbuzz/src/hb-ot-var-common.hh

@@ -231,9 +231,9 @@ struct tuple_delta_t
   /* indices_length = point_count, indice[i] = 1 means point i is referenced */
   /* indices_length = point_count, indice[i] = 1 means point i is referenced */
   hb_vector_t<bool> indices;
   hb_vector_t<bool> indices;
 
 
-  hb_vector_t<double> deltas_x;
+  hb_vector_t<float> deltas_x;
   /* empty for cvar tuples */
   /* empty for cvar tuples */
-  hb_vector_t<double> deltas_y;
+  hb_vector_t<float> deltas_y;
 
 
   /* compiled data: header and deltas
   /* compiled data: header and deltas
    * compiled point data is saved in a hashmap within tuple_variations_t cause
    * compiled point data is saved in a hashmap within tuple_variations_t cause
@@ -299,9 +299,9 @@ struct tuple_delta_t
     return *this;
     return *this;
   }
   }
 
 
-  tuple_delta_t& operator *= (double scalar)
+  tuple_delta_t& operator *= (float scalar)
   {
   {
-    if (scalar == 1.0)
+    if (scalar == 1.0f)
       return *this;
       return *this;
 
 
     unsigned num = indices.length;
     unsigned num = indices.length;
@@ -514,9 +514,9 @@ struct tuple_delta_t
   bool compile_deltas ()
   bool compile_deltas ()
   { return compile_deltas (indices, deltas_x, deltas_y, compiled_deltas); }
   { return compile_deltas (indices, deltas_x, deltas_y, compiled_deltas); }
 
 
-  static bool compile_deltas (const hb_vector_t<bool> &point_indices,
-			      const hb_vector_t<double> &x_deltas,
-			      const hb_vector_t<double> &y_deltas,
+  static bool compile_deltas (hb_array_t<const bool> point_indices,
+			      hb_array_t<const float> x_deltas,
+			      hb_array_t<const float> y_deltas,
 			      hb_vector_t<unsigned char> &compiled_deltas /* OUT */)
 			      hb_vector_t<unsigned char> &compiled_deltas /* OUT */)
   {
   {
     hb_vector_t<int> rounded_deltas;
     hb_vector_t<int> rounded_deltas;
@@ -629,11 +629,11 @@ struct tuple_delta_t
           deltas_x.arrayZ[i] = infer_delta ((double) orig_points.arrayZ[i].x,
           deltas_x.arrayZ[i] = infer_delta ((double) orig_points.arrayZ[i].x,
                                             (double) orig_points.arrayZ[prev].x,
                                             (double) orig_points.arrayZ[prev].x,
                                             (double) orig_points.arrayZ[next].x,
                                             (double) orig_points.arrayZ[next].x,
-                                            deltas_x.arrayZ[prev], deltas_x.arrayZ[next]);
+                                            (double) deltas_x.arrayZ[prev], (double) deltas_x.arrayZ[next]);
           deltas_y.arrayZ[i] = infer_delta ((double) orig_points.arrayZ[i].y,
           deltas_y.arrayZ[i] = infer_delta ((double) orig_points.arrayZ[i].y,
                                             (double) orig_points.arrayZ[prev].y,
                                             (double) orig_points.arrayZ[prev].y,
                                             (double) orig_points.arrayZ[next].y,
                                             (double) orig_points.arrayZ[next].y,
-                                            deltas_y.arrayZ[prev], deltas_y.arrayZ[next]);
+                                            (double) deltas_y.arrayZ[prev], (double) deltas_y.arrayZ[next]);
           inferred_idxes.add (i);
           inferred_idxes.add (i);
           if (--unref_count == 0) goto no_more_gaps;
           if (--unref_count == 0) goto no_more_gaps;
         }
         }
@@ -692,7 +692,7 @@ struct tuple_delta_t
 
 
     if (ref_count == count) return true;
     if (ref_count == count) return true;
 
 
-    hb_vector_t<double> opt_deltas_x, opt_deltas_y;
+    hb_vector_t<float> opt_deltas_x, opt_deltas_y;
     bool is_comp_glyph_wo_deltas = (is_composite && ref_count == 0);
     bool is_comp_glyph_wo_deltas = (is_composite && ref_count == 0);
     if (is_comp_glyph_wo_deltas)
     if (is_comp_glyph_wo_deltas)
     {
     {
@@ -841,6 +841,7 @@ struct tuple_delta_t
   { return (i >= end) ? start : (i + 1); }
   { return (i >= end) ? start : (i + 1); }
 };
 };
 
 
+template <typename OffType = HBUINT16>
 struct TupleVariationData
 struct TupleVariationData
 {
 {
   bool sanitize (hb_sanitize_context_t *c) const
   bool sanitize (hb_sanitize_context_t *c) const
@@ -875,7 +876,7 @@ struct TupleVariationData
 
 
     private:
     private:
     /* referenced point set->compiled point data map */
     /* referenced point set->compiled point data map */
-    hb_hashmap_t<const hb_vector_t<bool>*, hb_vector_t<char>> point_data_map;
+    hb_hashmap_t<const hb_vector_t<bool>*, hb_vector_t<unsigned char>> point_data_map;
     /* referenced point set-> count map, used in finding shared points */
     /* referenced point set-> count map, used in finding shared points */
     hb_hashmap_t<const hb_vector_t<bool>*, unsigned> point_set_count_map;
     hb_hashmap_t<const hb_vector_t<bool>*, unsigned> point_set_count_map;
 
 
@@ -883,11 +884,11 @@ struct TupleVariationData
      * shared_points_bytes is a pointer to some value in the point_data_map,
      * shared_points_bytes is a pointer to some value in the point_data_map,
      * which will be freed during map destruction. Save it for serialization, so
      * which will be freed during map destruction. Save it for serialization, so
      * no need to do find_shared_points () again */
      * no need to do find_shared_points () again */
-    hb_vector_t<char> *shared_points_bytes = nullptr;
+    hb_vector_t<unsigned char> *shared_points_bytes = nullptr;
 
 
-    /* total compiled byte size as TupleVariationData format, initialized to its
-     * min_size: 4 */
-    unsigned compiled_byte_size = 4;
+    /* total compiled byte size as TupleVariationData format, initialized to 0 */
+    unsigned compiled_byte_size = 0;
+    bool needs_padding = false;
 
 
     /* for gvar iup delta optimization: whether this is a composite glyph */
     /* for gvar iup delta optimization: whether this is a composite glyph */
     bool is_composite = false;
     bool is_composite = false;
@@ -1219,12 +1220,21 @@ struct TupleVariationData
     bool compile_bytes (const hb_map_t& axes_index_map,
     bool compile_bytes (const hb_map_t& axes_index_map,
                         const hb_map_t& axes_old_index_tag_map,
                         const hb_map_t& axes_old_index_tag_map,
                         bool use_shared_points,
                         bool use_shared_points,
+                        bool is_gvar = false,
                         const hb_hashmap_t<const hb_vector_t<char>*, unsigned>* shared_tuples_idx_map = nullptr)
                         const hb_hashmap_t<const hb_vector_t<char>*, unsigned>* shared_tuples_idx_map = nullptr)
     {
     {
+      // return true for empty glyph
+      if (!tuple_vars)
+        return true;
+
       // compile points set and store data in hashmap
       // compile points set and store data in hashmap
       if (!compile_all_point_sets ())
       if (!compile_all_point_sets ())
         return false;
         return false;
 
 
+      /* total compiled byte size as TupleVariationData format, initialized to its
+       * min_size: 4 */
+      compiled_byte_size += 4;
+
       if (use_shared_points)
       if (use_shared_points)
       {
       {
         find_shared_points ();
         find_shared_points ();
@@ -1235,7 +1245,7 @@ struct TupleVariationData
       for (auto& tuple: tuple_vars)
       for (auto& tuple: tuple_vars)
       {
       {
         const hb_vector_t<bool>* points_set = &(tuple.indices);
         const hb_vector_t<bool>* points_set = &(tuple.indices);
-        hb_vector_t<char> *points_data;
+        hb_vector_t<unsigned char> *points_data;
         if (unlikely (!point_data_map.has (points_set, &points_data)))
         if (unlikely (!point_data_map.has (points_set, &points_data)))
           return false;
           return false;
 
 
@@ -1253,6 +1263,13 @@ struct TupleVariationData
           return false;
           return false;
         compiled_byte_size += tuple.compiled_tuple_header.length + points_data_length + tuple.compiled_deltas.length;
         compiled_byte_size += tuple.compiled_tuple_header.length + points_data_length + tuple.compiled_deltas.length;
       }
       }
+
+      if (is_gvar && (compiled_byte_size % 2))
+      {
+        needs_padding = true;
+        compiled_byte_size += 1;
+      }
+
       return true;
       return true;
     }
     }
 
 
@@ -1273,20 +1290,20 @@ struct TupleVariationData
       TRACE_SERIALIZE (this);
       TRACE_SERIALIZE (this);
       if (is_gvar && shared_points_bytes)
       if (is_gvar && shared_points_bytes)
       {
       {
-        hb_bytes_t s (shared_points_bytes->arrayZ, shared_points_bytes->length);
+        hb_ubytes_t s (shared_points_bytes->arrayZ, shared_points_bytes->length);
         s.copy (c);
         s.copy (c);
       }
       }
 
 
       for (const auto& tuple: tuple_vars)
       for (const auto& tuple: tuple_vars)
       {
       {
         const hb_vector_t<bool>* points_set = &(tuple.indices);
         const hb_vector_t<bool>* points_set = &(tuple.indices);
-        hb_vector_t<char> *point_data;
+        hb_vector_t<unsigned char> *point_data;
         if (!point_data_map.has (points_set, &point_data))
         if (!point_data_map.has (points_set, &point_data))
           return_trace (false);
           return_trace (false);
 
 
         if (!is_gvar || point_data != shared_points_bytes)
         if (!is_gvar || point_data != shared_points_bytes)
         {
         {
-          hb_bytes_t s (point_data->arrayZ, point_data->length);
+          hb_ubytes_t s (point_data->arrayZ, point_data->length);
           s.copy (c);
           s.copy (c);
         }
         }
 
 
@@ -1295,7 +1312,7 @@ struct TupleVariationData
       }
       }
 
 
       /* padding for gvar */
       /* padding for gvar */
-      if (is_gvar && (compiled_byte_size % 2))
+      if (is_gvar && needs_padding)
       {
       {
         HBUINT8 pad;
         HBUINT8 pad;
         pad = 0;
         pad = 0;
@@ -1505,15 +1522,16 @@ struct TupleVariationData
                                  * low 12 bits are the number of tuple variation tables
                                  * low 12 bits are the number of tuple variation tables
                                  * for this glyph. The number of tuple variation tables
                                  * for this glyph. The number of tuple variation tables
                                  * can be any number between 1 and 4095. */
                                  * can be any number between 1 and 4095. */
-  Offset16To<HBUINT8>
+  OffsetTo<HBUINT8, OffType>
                 data;           /* Offset from the start of the base table
                 data;           /* Offset from the start of the base table
                                  * to the serialized data. */
                                  * to the serialized data. */
   /* TupleVariationHeader tupleVariationHeaders[] *//* Array of tuple variation headers. */
   /* TupleVariationHeader tupleVariationHeaders[] *//* Array of tuple variation headers. */
   public:
   public:
-  DEFINE_SIZE_MIN (4);
+  DEFINE_SIZE_MIN (2 + OffType::static_size);
 };
 };
 
 
-using tuple_variations_t = TupleVariationData::tuple_variations_t;
+// TODO: Move tuple_variations_t to outside of TupleVariationData
+using tuple_variations_t = TupleVariationData<HBUINT16>::tuple_variations_t;
 struct item_variations_t
 struct item_variations_t
 {
 {
   using region_t = const hb_hashmap_t<hb_tag_t, Triple>*;
   using region_t = const hb_hashmap_t<hb_tag_t, Triple>*;

+ 13 - 13
thirdparty/harfbuzz/src/hb-ot-var-cvar-table.hh

@@ -50,7 +50,7 @@ struct cvar
 		  tupleVariationData.sanitize (c));
 		  tupleVariationData.sanitize (c));
   }
   }
 
 
-  const TupleVariationData* get_tuple_var_data (void) const
+  const TupleVariationData<>* get_tuple_var_data (void) const
   { return &tupleVariationData; }
   { return &tupleVariationData; }
 
 
   bool decompile_tuple_variations (unsigned axis_count,
   bool decompile_tuple_variations (unsigned axis_count,
@@ -58,12 +58,12 @@ struct cvar
                                    hb_blob_t *blob,
                                    hb_blob_t *blob,
                                    bool is_gvar,
                                    bool is_gvar,
                                    const hb_map_t *axes_old_index_tag_map,
                                    const hb_map_t *axes_old_index_tag_map,
-                                   TupleVariationData::tuple_variations_t& tuple_variations /* OUT */) const
+                                   TupleVariationData<>::tuple_variations_t& tuple_variations /* OUT */) const
   {
   {
     hb_vector_t<unsigned> shared_indices;
     hb_vector_t<unsigned> shared_indices;
-    TupleVariationData::tuple_iterator_t iterator;
+    TupleVariationData<>::tuple_iterator_t iterator;
     hb_bytes_t var_data_bytes = blob->as_bytes ().sub_array (4);
     hb_bytes_t var_data_bytes = blob->as_bytes ().sub_array (4);
-    if (!TupleVariationData::get_tuple_iterator (var_data_bytes, axis_count, this,
+    if (!TupleVariationData<>::get_tuple_iterator (var_data_bytes, axis_count, this,
                                                  shared_indices, &iterator))
                                                  shared_indices, &iterator))
       return false;
       return false;
     
     
@@ -77,16 +77,16 @@ struct cvar
   static bool calculate_cvt_deltas (unsigned axis_count,
   static bool calculate_cvt_deltas (unsigned axis_count,
                                     hb_array_t<int> coords,
                                     hb_array_t<int> coords,
                                     unsigned num_cvt_item,
                                     unsigned num_cvt_item,
-                                    const TupleVariationData *tuple_var_data,
+                                    const TupleVariationData<> *tuple_var_data,
                                     const void *base,
                                     const void *base,
                                     hb_vector_t<float>& cvt_deltas /* OUT */)
                                     hb_vector_t<float>& cvt_deltas /* OUT */)
   {
   {
     if (!coords) return true;
     if (!coords) return true;
     hb_vector_t<unsigned> shared_indices;
     hb_vector_t<unsigned> shared_indices;
-    TupleVariationData::tuple_iterator_t iterator;
+    TupleVariationData<>::tuple_iterator_t iterator;
     unsigned var_data_length = tuple_var_data->get_size (axis_count);
     unsigned var_data_length = tuple_var_data->get_size (axis_count);
     hb_bytes_t var_data_bytes = hb_bytes_t (reinterpret_cast<const char*> (tuple_var_data), var_data_length);
     hb_bytes_t var_data_bytes = hb_bytes_t (reinterpret_cast<const char*> (tuple_var_data), var_data_length);
-    if (!TupleVariationData::get_tuple_iterator (var_data_bytes, axis_count, base,
+    if (!TupleVariationData<>::get_tuple_iterator (var_data_bytes, axis_count, base,
                                                  shared_indices, &iterator))
                                                  shared_indices, &iterator))
       return true; /* isn't applied at all */
       return true; /* isn't applied at all */
 
 
@@ -107,14 +107,14 @@ struct cvar
 
 
       bool has_private_points = iterator.current_tuple->has_private_points ();
       bool has_private_points = iterator.current_tuple->has_private_points ();
       if (has_private_points &&
       if (has_private_points &&
-          !TupleVariationData::decompile_points (p, private_indices, end))
+          !TupleVariationData<>::decompile_points (p, private_indices, end))
         return false;
         return false;
       const hb_vector_t<unsigned int> &indices = has_private_points ? private_indices : shared_indices;
       const hb_vector_t<unsigned int> &indices = has_private_points ? private_indices : shared_indices;
 
 
       bool apply_to_all = (indices.length == 0);
       bool apply_to_all = (indices.length == 0);
       unsigned num_deltas = apply_to_all ? num_cvt_item : indices.length;
       unsigned num_deltas = apply_to_all ? num_cvt_item : indices.length;
       if (unlikely (!unpacked_deltas.resize (num_deltas, false))) return false;
       if (unlikely (!unpacked_deltas.resize (num_deltas, false))) return false;
-      if (unlikely (!TupleVariationData::decompile_deltas (p, unpacked_deltas, end))) return false;
+      if (unlikely (!TupleVariationData<>::decompile_deltas (p, unpacked_deltas, end))) return false;
 
 
       for (unsigned int i = 0; i < num_deltas; i++)
       for (unsigned int i = 0; i < num_deltas; i++)
       {
       {
@@ -129,7 +129,7 @@ struct cvar
   }
   }
   
   
   bool serialize (hb_serialize_context_t *c,
   bool serialize (hb_serialize_context_t *c,
-                  TupleVariationData::tuple_variations_t& tuple_variations) const
+                  TupleVariationData<>::tuple_variations_t& tuple_variations) const
   {
   {
     TRACE_SERIALIZE (this);
     TRACE_SERIALIZE (this);
     if (!tuple_variations) return_trace (false);
     if (!tuple_variations) return_trace (false);
@@ -144,7 +144,7 @@ struct cvar
     if (c->plan->all_axes_pinned)
     if (c->plan->all_axes_pinned)
       return_trace (false);
       return_trace (false);
 
 
-    OT::TupleVariationData::tuple_variations_t tuple_variations;
+    OT::TupleVariationData<>::tuple_variations_t tuple_variations;
     unsigned axis_count = c->plan->axes_old_index_tag_map.get_population ();
     unsigned axis_count = c->plan->axes_old_index_tag_map.get_population ();
 
 
     const hb_tag_t cvt = HB_TAG('c','v','t',' ');
     const hb_tag_t cvt = HB_TAG('c','v','t',' ');
@@ -169,7 +169,7 @@ struct cvar
   }
   }
 
 
   static bool add_cvt_and_apply_deltas (hb_subset_plan_t *plan,
   static bool add_cvt_and_apply_deltas (hb_subset_plan_t *plan,
-                                        const TupleVariationData *tuple_var_data,
+                                        const TupleVariationData<> *tuple_var_data,
                                         const void *base)
                                         const void *base)
   {
   {
     const hb_tag_t cvt = HB_TAG('c','v','t',' ');
     const hb_tag_t cvt = HB_TAG('c','v','t',' ');
@@ -209,7 +209,7 @@ struct cvar
   protected:
   protected:
   FixedVersion<>version;		/* Version of the CVT variation table
   FixedVersion<>version;		/* Version of the CVT variation table
 					 * initially set to 0x00010000u */
 					 * initially set to 0x00010000u */
-  TupleVariationData tupleVariationData; /* TupleVariationDate for cvar table */
+  TupleVariationData<> tupleVariationData; /* TupleVariationDate for cvar table */
   public:
   public:
   DEFINE_SIZE_MIN (8);
   DEFINE_SIZE_MIN (8);
 };
 };

+ 90 - 52
thirdparty/harfbuzz/src/hb-ot-var-gvar-table.hh

@@ -28,6 +28,7 @@
 #ifndef HB_OT_VAR_GVAR_TABLE_HH
 #ifndef HB_OT_VAR_GVAR_TABLE_HH
 #define HB_OT_VAR_GVAR_TABLE_HH
 #define HB_OT_VAR_GVAR_TABLE_HH
 
 
+#include "hb-decycler.hh"
 #include "hb-open-type.hh"
 #include "hb-open-type.hh"
 #include "hb-ot-var-common.hh"
 #include "hb-ot-var-common.hh"
 
 
@@ -36,15 +37,37 @@
  * https://docs.microsoft.com/en-us/typography/opentype/spec/gvar
  * https://docs.microsoft.com/en-us/typography/opentype/spec/gvar
  */
  */
 #define HB_OT_TAG_gvar HB_TAG('g','v','a','r')
 #define HB_OT_TAG_gvar HB_TAG('g','v','a','r')
+#define HB_OT_TAG_GVAR HB_TAG('G','V','A','R')
 
 
-namespace OT {
+struct hb_glyf_scratch_t
+{
+  // glyf
+  contour_point_vector_t all_points;
+  contour_point_vector_t comp_points;
+  hb_decycler_t decycler;
+
+  // gvar
+  contour_point_vector_t orig_points;
+  hb_vector_t<int> x_deltas;
+  hb_vector_t<int> y_deltas;
+  contour_point_vector_t deltas;
+  hb_vector_t<unsigned int> shared_indices;
+  hb_vector_t<unsigned int> private_indices;
+
+  // VARC
+  hb_vector_t<unsigned> axisIndices;
+  hb_vector_t<float> axisValues;
+};
 
 
-struct GlyphVariationData : TupleVariationData
-{};
+namespace OT {
 
 
+template <typename OffsetType>
 struct glyph_variations_t
 struct glyph_variations_t
 {
 {
-  using tuple_variations_t = TupleVariationData::tuple_variations_t;
+  // TODO: Move tuple_variations_t to outside of TupleVariationData
+  using tuple_variations_t = typename TupleVariationData<OffsetType>::tuple_variations_t;
+  using GlyphVariationData = TupleVariationData<OffsetType>;
+
   hb_vector_t<tuple_variations_t> glyph_variations;
   hb_vector_t<tuple_variations_t> glyph_variations;
 
 
   hb_vector_t<char> compiled_shared_tuples;
   hb_vector_t<char> compiled_shared_tuples;
@@ -72,7 +95,7 @@ struct glyph_variations_t
                                     const hb_subset_plan_t *plan,
                                     const hb_subset_plan_t *plan,
                                     const hb_hashmap_t<hb_codepoint_t, hb_bytes_t>& new_gid_var_data_map)
                                     const hb_hashmap_t<hb_codepoint_t, hb_bytes_t>& new_gid_var_data_map)
   {
   {
-    if (unlikely (!glyph_variations.alloc (plan->new_to_old_gid_list.length, true)))
+    if (unlikely (!glyph_variations.alloc_exact (plan->new_to_old_gid_list.length)))
       return false;
       return false;
 
 
     auto it = hb_iter (plan->new_to_old_gid_list);
     auto it = hb_iter (plan->new_to_old_gid_list);
@@ -86,10 +109,11 @@ struct glyph_variations_t
       hb_bytes_t var_data = new_gid_var_data_map.get (new_gid);
       hb_bytes_t var_data = new_gid_var_data_map.get (new_gid);
 
 
       const GlyphVariationData* p = reinterpret_cast<const GlyphVariationData*> (var_data.arrayZ);
       const GlyphVariationData* p = reinterpret_cast<const GlyphVariationData*> (var_data.arrayZ);
-      hb_vector_t<unsigned> shared_indices;
-      GlyphVariationData::tuple_iterator_t iterator;
+      typename GlyphVariationData::tuple_iterator_t iterator;
       tuple_variations_t tuple_vars;
       tuple_variations_t tuple_vars;
 
 
+      hb_vector_t<unsigned> shared_indices;
+
       /* in case variation data is empty, push an empty struct into the vector,
       /* in case variation data is empty, push an empty struct into the vector,
        * keep the vector in sync with the new_to_old_gid_list */
        * keep the vector in sync with the new_to_old_gid_list */
       if (!var_data || ! p->has_data () || !all_contour_points->length ||
       if (!var_data || ! p->has_data () || !all_contour_points->length ||
@@ -140,6 +164,7 @@ struct glyph_variations_t
     for (tuple_variations_t& vars: glyph_variations)
     for (tuple_variations_t& vars: glyph_variations)
       if (!vars.compile_bytes (axes_index_map, axes_old_index_tag_map,
       if (!vars.compile_bytes (axes_index_map, axes_old_index_tag_map,
                                true, /* use shared points*/
                                true, /* use shared points*/
+                               true,
                                &shared_tuples_idx_map))
                                &shared_tuples_idx_map))
         return false;
         return false;
 
 
@@ -258,7 +283,7 @@ struct glyph_variations_t
     hb_codepoint_t last_gid = 0;
     hb_codepoint_t last_gid = 0;
     unsigned idx = 0;
     unsigned idx = 0;
 
 
-    TupleVariationData* cur_glyph = c->start_embed<TupleVariationData> ();
+    GlyphVariationData* cur_glyph = c->start_embed<GlyphVariationData> ();
     if (!cur_glyph) return_trace (false);
     if (!cur_glyph) return_trace (false);
     for (auto &_ : it)
     for (auto &_ : it)
     {
     {
@@ -272,7 +297,7 @@ struct glyph_variations_t
 
 
       if (idx >= glyph_variations.length) return_trace (false);
       if (idx >= glyph_variations.length) return_trace (false);
       if (!cur_glyph->serialize (c, true, glyph_variations[idx])) return_trace (false);
       if (!cur_glyph->serialize (c, true, glyph_variations[idx])) return_trace (false);
-      TupleVariationData* next_glyph = c->start_embed<TupleVariationData> ();
+      GlyphVariationData* next_glyph = c->start_embed<GlyphVariationData> ();
       glyph_offset += (char *) next_glyph - (char *) cur_glyph;
       glyph_offset += (char *) next_glyph - (char *) cur_glyph;
 
 
       if (long_offset)
       if (long_offset)
@@ -295,9 +320,14 @@ struct glyph_variations_t
   }
   }
 };
 };
 
 
-struct gvar
+template <typename GidOffsetType, unsigned TableTag>
+struct gvar_GVAR
 {
 {
-  static constexpr hb_tag_t tableTag = HB_OT_TAG_gvar;
+  static constexpr hb_tag_t tableTag = TableTag;
+
+  using GlyphVariationData = TupleVariationData<GidOffsetType>;
+
+  bool has_data () const { return version.to_int () != 0; }
 
 
   bool sanitize_shallow (hb_sanitize_context_t *c) const
   bool sanitize_shallow (hb_sanitize_context_t *c) const
   {
   {
@@ -316,7 +346,7 @@ struct gvar
   { return sanitize_shallow (c); }
   { return sanitize_shallow (c); }
 
 
   bool decompile_glyph_variations (hb_subset_context_t *c,
   bool decompile_glyph_variations (hb_subset_context_t *c,
-                                   glyph_variations_t& glyph_vars /* OUT */) const
+                                   glyph_variations_t<GidOffsetType>& glyph_vars /* OUT */) const
   {
   {
     hb_hashmap_t<hb_codepoint_t, hb_bytes_t> new_gid_var_data_map;
     hb_hashmap_t<hb_codepoint_t, hb_bytes_t> new_gid_var_data_map;
     auto it = hb_iter (c->plan->new_to_old_gid_list);
     auto it = hb_iter (c->plan->new_to_old_gid_list);
@@ -343,14 +373,14 @@ struct gvar
   template<typename Iterator,
   template<typename Iterator,
            hb_requires (hb_is_iterator (Iterator))>
            hb_requires (hb_is_iterator (Iterator))>
   bool serialize (hb_serialize_context_t *c,
   bool serialize (hb_serialize_context_t *c,
-                  const glyph_variations_t& glyph_vars,
+                  const glyph_variations_t<GidOffsetType>& glyph_vars,
                   Iterator it,
                   Iterator it,
                   unsigned axis_count,
                   unsigned axis_count,
                   unsigned num_glyphs,
                   unsigned num_glyphs,
                   bool force_long_offsets) const
                   bool force_long_offsets) const
   {
   {
     TRACE_SERIALIZE (this);
     TRACE_SERIALIZE (this);
-    gvar *out = c->allocate_min<gvar> ();
+    gvar_GVAR *out = c->allocate_min<gvar_GVAR> ();
     if (unlikely (!out)) return_trace (false);
     if (unlikely (!out)) return_trace (false);
 
 
     out->version.major = 1;
     out->version.major = 1;
@@ -392,7 +422,7 @@ struct gvar
   bool instantiate (hb_subset_context_t *c) const
   bool instantiate (hb_subset_context_t *c) const
   {
   {
     TRACE_SUBSET (this);
     TRACE_SUBSET (this);
-    glyph_variations_t glyph_vars;
+    glyph_variations_t<GidOffsetType> glyph_vars;
     if (!decompile_glyph_variations (c, glyph_vars))
     if (!decompile_glyph_variations (c, glyph_vars))
       return_trace (false);
       return_trace (false);
 
 
@@ -422,7 +452,7 @@ struct gvar
 
 
     unsigned glyph_count = version.to_int () ? c->plan->source->get_num_glyphs () : 0;
     unsigned glyph_count = version.to_int () ? c->plan->source->get_num_glyphs () : 0;
 
 
-    gvar *out = c->serializer->allocate_min<gvar> ();
+    gvar_GVAR *out = c->serializer->allocate_min<gvar_GVAR> ();
     if (unlikely (!out)) return_trace (false);
     if (unlikely (!out)) return_trace (false);
 
 
     out->version.major = 1;
     out->version.major = 1;
@@ -556,9 +586,11 @@ struct gvar
   public:
   public:
   struct accelerator_t
   struct accelerator_t
   {
   {
+    bool has_data () const { return table->has_data (); }
+
     accelerator_t (hb_face_t *face)
     accelerator_t (hb_face_t *face)
     {
     {
-      table = hb_sanitize_context_t ().reference_table<gvar> (face);
+      table = hb_sanitize_context_t ().reference_table<gvar_GVAR> (face);
       /* If sanitize failed, set glyphCount to 0. */
       /* If sanitize failed, set glyphCount to 0. */
       glyphCount = table->version.to_int () ? face->get_num_glyphs () : 0;
       glyphCount = table->version.to_int () ? face->get_num_glyphs () : 0;
 
 
@@ -626,35 +658,40 @@ struct gvar
     bool apply_deltas_to_points (hb_codepoint_t glyph,
     bool apply_deltas_to_points (hb_codepoint_t glyph,
 				 hb_array_t<const int> coords,
 				 hb_array_t<const int> coords,
 				 const hb_array_t<contour_point_t> points,
 				 const hb_array_t<contour_point_t> points,
+				 hb_glyf_scratch_t &scratch,
 				 bool phantom_only = false) const
 				 bool phantom_only = false) const
     {
     {
       if (unlikely (glyph >= glyphCount)) return true;
       if (unlikely (glyph >= glyphCount)) return true;
 
 
       hb_bytes_t var_data_bytes = table->get_glyph_var_data_bytes (table.get_blob (), glyphCount, glyph);
       hb_bytes_t var_data_bytes = table->get_glyph_var_data_bytes (table.get_blob (), glyphCount, glyph);
       if (!var_data_bytes.as<GlyphVariationData> ()->has_data ()) return true;
       if (!var_data_bytes.as<GlyphVariationData> ()->has_data ()) return true;
-      hb_vector_t<unsigned int> shared_indices;
-      GlyphVariationData::tuple_iterator_t iterator;
+
+      auto &shared_indices = scratch.shared_indices;
+      shared_indices.clear ();
+
+      typename GlyphVariationData::tuple_iterator_t iterator;
       if (!GlyphVariationData::get_tuple_iterator (var_data_bytes, table->axisCount,
       if (!GlyphVariationData::get_tuple_iterator (var_data_bytes, table->axisCount,
 						   var_data_bytes.arrayZ,
 						   var_data_bytes.arrayZ,
 						   shared_indices, &iterator))
 						   shared_indices, &iterator))
 	return true; /* so isn't applied at all */
 	return true; /* so isn't applied at all */
 
 
       /* Save original points for inferred delta calculation */
       /* Save original points for inferred delta calculation */
-      contour_point_vector_t orig_points_vec; // Populated lazily
+      auto &orig_points_vec = scratch.orig_points;
+      orig_points_vec.clear (); // Populated lazily
       auto orig_points = orig_points_vec.as_array ();
       auto orig_points = orig_points_vec.as_array ();
 
 
       /* flag is used to indicate referenced point */
       /* flag is used to indicate referenced point */
-      contour_point_vector_t deltas_vec; // Populated lazily
+      auto &deltas_vec = scratch.deltas;
+      deltas_vec.clear (); // Populated lazily
       auto deltas = deltas_vec.as_array ();
       auto deltas = deltas_vec.as_array ();
 
 
-      hb_vector_t<unsigned> end_points; // Populated lazily
-
       unsigned num_coords = table->axisCount;
       unsigned num_coords = table->axisCount;
       hb_array_t<const F2DOT14> shared_tuples = (table+table->sharedTuples).as_array (table->sharedTupleCount * num_coords);
       hb_array_t<const F2DOT14> shared_tuples = (table+table->sharedTuples).as_array (table->sharedTupleCount * num_coords);
 
 
-      hb_vector_t<unsigned int> private_indices;
-      hb_vector_t<int> x_deltas;
-      hb_vector_t<int> y_deltas;
+      auto &private_indices = scratch.private_indices;
+      auto &x_deltas = scratch.x_deltas;
+      auto &y_deltas = scratch.y_deltas;
+
       unsigned count = points.length;
       unsigned count = points.length;
       bool flush = false;
       bool flush = false;
       do
       do
@@ -725,8 +762,8 @@ struct gvar
 	    if (phantom_only && pt_index < count - 4) continue;
 	    if (phantom_only && pt_index < count - 4) continue;
 	    auto &delta = deltas.arrayZ[pt_index];
 	    auto &delta = deltas.arrayZ[pt_index];
 	    delta.flag = 1;	/* this point is referenced, i.e., explicit deltas specified */
 	    delta.flag = 1;	/* this point is referenced, i.e., explicit deltas specified */
-	    delta.x += x_deltas.arrayZ[i] * scalar;
-	    delta.y += y_deltas.arrayZ[i] * scalar;
+	    delta.add_delta (x_deltas.arrayZ[i] * scalar,
+			     y_deltas.arrayZ[i] * scalar);
 	  }
 	  }
 	}
 	}
 	else
 	else
@@ -737,10 +774,9 @@ struct gvar
 	    if (apply_to_all)
 	    if (apply_to_all)
 	      for (unsigned int i = phantom_only ? count - 4 : 0; i < count; i++)
 	      for (unsigned int i = phantom_only ? count - 4 : 0; i < count; i++)
 	      {
 	      {
-		unsigned int pt_index = i;
-		auto &delta = deltas.arrayZ[pt_index];
-		delta.x += x_deltas.arrayZ[i] * scalar;
-		delta.y += y_deltas.arrayZ[i] * scalar;
+		auto &delta = deltas.arrayZ[i];
+		delta.add_delta (x_deltas.arrayZ[i] * scalar,
+				 y_deltas.arrayZ[i] * scalar);
 	      }
 	      }
 	    else
 	    else
 	      for (unsigned int i = 0; i < num_deltas; i++)
 	      for (unsigned int i = 0; i < num_deltas; i++)
@@ -750,8 +786,8 @@ struct gvar
 		if (phantom_only && pt_index < count - 4) continue;
 		if (phantom_only && pt_index < count - 4) continue;
 		auto &delta = deltas.arrayZ[pt_index];
 		auto &delta = deltas.arrayZ[pt_index];
 		delta.flag = 1;	/* this point is referenced, i.e., explicit deltas specified */
 		delta.flag = 1;	/* this point is referenced, i.e., explicit deltas specified */
-		delta.x += x_deltas.arrayZ[i] * scalar;
-		delta.y += y_deltas.arrayZ[i] * scalar;
+		delta.add_delta (x_deltas.arrayZ[i] * scalar,
+				 y_deltas.arrayZ[i] * scalar);
 	      }
 	      }
 	  }
 	  }
 	  else
 	  else
@@ -759,10 +795,9 @@ struct gvar
 	    if (apply_to_all)
 	    if (apply_to_all)
 	      for (unsigned int i = phantom_only ? count - 4 : 0; i < count; i++)
 	      for (unsigned int i = phantom_only ? count - 4 : 0; i < count; i++)
 	      {
 	      {
-		unsigned int pt_index = i;
-		auto &delta = deltas.arrayZ[pt_index];
-		delta.x += x_deltas.arrayZ[i];
-		delta.y += y_deltas.arrayZ[i];
+		auto &delta = deltas.arrayZ[i];
+		delta.add_delta (x_deltas.arrayZ[i],
+				 y_deltas.arrayZ[i]);
 	      }
 	      }
 	    else
 	    else
 	      for (unsigned int i = 0; i < num_deltas; i++)
 	      for (unsigned int i = 0; i < num_deltas; i++)
@@ -772,8 +807,8 @@ struct gvar
 		if (phantom_only && pt_index < count - 4) continue;
 		if (phantom_only && pt_index < count - 4) continue;
 		auto &delta = deltas.arrayZ[pt_index];
 		auto &delta = deltas.arrayZ[pt_index];
 		delta.flag = 1;	/* this point is referenced, i.e., explicit deltas specified */
 		delta.flag = 1;	/* this point is referenced, i.e., explicit deltas specified */
-		delta.x += x_deltas.arrayZ[i];
-		delta.y += y_deltas.arrayZ[i];
+		delta.add_delta (x_deltas.arrayZ[i],
+				 y_deltas.arrayZ[i]);
 	      }
 	      }
 	  }
 	  }
 	}
 	}
@@ -781,17 +816,14 @@ struct gvar
 	/* infer deltas for unreferenced points */
 	/* infer deltas for unreferenced points */
 	if (!apply_to_all && !phantom_only)
 	if (!apply_to_all && !phantom_only)
 	{
 	{
-	  if (!end_points)
-	  {
-	    for (unsigned i = 0; i < count; ++i)
-	      if (points.arrayZ[i].is_end_point)
-		end_points.push (i);
-	    if (unlikely (end_points.in_error ())) return false;
-	  }
-
 	  unsigned start_point = 0;
 	  unsigned start_point = 0;
-	  for (unsigned end_point : end_points)
+	  unsigned end_point = 0;
+	  while (true)
 	  {
 	  {
+	    while (end_point < count && !points.arrayZ[end_point].is_end_point)
+	      end_point++;
+	    if (unlikely (end_point == count)) break;
+
 	    /* Check the number of unreferenced points in a contour. If no unref points or no ref points, nothing to do. */
 	    /* Check the number of unreferenced points in a contour. If no unref points or no ref points, nothing to do. */
 	    unsigned unref_count = 0;
 	    unsigned unref_count = 0;
 	    for (unsigned i = start_point; i < end_point + 1; i++)
 	    for (unsigned i = start_point; i < end_point + 1; i++)
@@ -834,7 +866,7 @@ struct gvar
 	      }
 	      }
 	    }
 	    }
 	  no_more_gaps:
 	  no_more_gaps:
-	    start_point = end_point + 1;
+	    start_point = end_point = end_point + 1;
 	  }
 	  }
 	}
 	}
 
 
@@ -854,7 +886,7 @@ struct gvar
     unsigned int get_axis_count () const { return table->axisCount; }
     unsigned int get_axis_count () const { return table->axisCount; }
 
 
     private:
     private:
-    hb_blob_ptr_t<gvar> table;
+    hb_blob_ptr_t<gvar_GVAR> table;
     unsigned glyphCount;
     unsigned glyphCount;
     hb_vector_t<hb_pair_t<int, int>> shared_tuple_active_idx;
     hb_vector_t<hb_pair_t<int, int>> shared_tuple_active_idx;
   };
   };
@@ -872,7 +904,7 @@ struct gvar
   NNOffset32To<UnsizedArrayOf<F2DOT14>>
   NNOffset32To<UnsizedArrayOf<F2DOT14>>
 		sharedTuples;	/* Offset from the start of this table to the shared tuple records.
 		sharedTuples;	/* Offset from the start of this table to the shared tuple records.
 				 * Array of tuple records shared across all glyph variation data tables. */
 				 * Array of tuple records shared across all glyph variation data tables. */
-  HBUINT16	glyphCountX;	/* The number of glyphs in this font. This must match the number of
+  GidOffsetType	glyphCountX;	/* The number of glyphs in this font. This must match the number of
 				 * glyphs stored elsewhere in the font. */
 				 * glyphs stored elsewhere in the font. */
   HBUINT16	flags;		/* Bit-field that gives the format of the offset array that follows.
   HBUINT16	flags;		/* Bit-field that gives the format of the offset array that follows.
 				 * If bit 0 is clear, the offsets are uint16; if bit 0 is set, the
 				 * If bit 0 is clear, the offsets are uint16; if bit 0 is set, the
@@ -887,9 +919,15 @@ struct gvar
   DEFINE_SIZE_ARRAY (20, offsetZ);
   DEFINE_SIZE_ARRAY (20, offsetZ);
 };
 };
 
 
+using gvar = gvar_GVAR<HBUINT16, HB_OT_TAG_gvar>;
+using GVAR = gvar_GVAR<HBUINT24, HB_OT_TAG_GVAR>;
+
 struct gvar_accelerator_t : gvar::accelerator_t {
 struct gvar_accelerator_t : gvar::accelerator_t {
   gvar_accelerator_t (hb_face_t *face) : gvar::accelerator_t (face) {}
   gvar_accelerator_t (hb_face_t *face) : gvar::accelerator_t (face) {}
 };
 };
+struct GVAR_accelerator_t : GVAR::accelerator_t {
+  GVAR_accelerator_t (hb_face_t *face) : GVAR::accelerator_t (face) {}
+};
 
 
 } /* namespace OT */
 } /* namespace OT */
 
 

+ 1 - 1
thirdparty/harfbuzz/src/hb-paint.h

@@ -146,7 +146,7 @@ typedef void (*hb_paint_pop_transform_func_t) (hb_paint_funcs_t *funcs,
  *
  *
  * A virtual method for the #hb_paint_funcs_t to render a color glyph by glyph index.
  * A virtual method for the #hb_paint_funcs_t to render a color glyph by glyph index.
  *
  *
- * Return value: %true if the glyph was painted, %false otherwise.
+ * Return value: `true` if the glyph was painted, `false` otherwise.
  *
  *
  * Since: 8.2.0
  * Since: 8.2.0
  */
  */

+ 2 - 2
thirdparty/harfbuzz/src/hb-sanitize.hh

@@ -72,8 +72,8 @@
  *
  *
  * === The sanitize() contract ===
  * === The sanitize() contract ===
  *
  *
- * The sanitize() method of each object type shall return true if it's safe to
- * call other methods of the object, and %false otherwise.
+ * The sanitize() method of each object type shall return `true` if it's safe to
+ * call other methods of the object, and `false` otherwise.
  *
  *
  * Note that what sanitize() checks for might align with what the specification
  * Note that what sanitize() checks for might align with what the specification
  * describes as valid table data, but does not have to be.  In particular, we
  * describes as valid table data, but does not have to be.  In particular, we

+ 12 - 15
thirdparty/harfbuzz/src/hb-serialize.hh

@@ -36,9 +36,7 @@
 #include "hb-map.hh"
 #include "hb-map.hh"
 #include "hb-pool.hh"
 #include "hb-pool.hh"
 
 
-#ifdef HB_EXPERIMENTAL_API
-#include "hb-subset-repacker.h"
-#endif
+#include "hb-subset-serialize.h"
 
 
 /*
 /*
  * Serialize
  * Serialize
@@ -75,21 +73,19 @@ struct hb_serialize_context_t
 
 
     object_t () = default;
     object_t () = default;
 
 
-#ifdef HB_EXPERIMENTAL_API
-    object_t (const hb_object_t &o)
+    object_t (const hb_subset_serialize_object_t &o)
     {
     {
       head = o.head;
       head = o.head;
       tail = o.tail;
       tail = o.tail;
       next = nullptr;
       next = nullptr;
-      real_links.alloc (o.num_real_links, true);
+      real_links.alloc_exact (o.num_real_links);
       for (unsigned i = 0 ; i < o.num_real_links; i++)
       for (unsigned i = 0 ; i < o.num_real_links; i++)
         real_links.push (o.real_links[i]);
         real_links.push (o.real_links[i]);
 
 
-      virtual_links.alloc (o.num_virtual_links, true);
+      virtual_links.alloc_exact (o.num_virtual_links);
       for (unsigned i = 0; i < o.num_virtual_links; i++)
       for (unsigned i = 0; i < o.num_virtual_links; i++)
         virtual_links.push (o.virtual_links[i]);
         virtual_links.push (o.virtual_links[i]);
     }
     }
-#endif
 
 
     bool add_virtual_link (objidx_t objidx)
     bool add_virtual_link (objidx_t objidx)
     {
     {
@@ -148,8 +144,7 @@ struct hb_serialize_context_t
 
 
       link_t () = default;
       link_t () = default;
 
 
-#ifdef HB_EXPERIMENTAL_API
-      link_t (const hb_link_t &o)
+      link_t (const hb_subset_serialize_link_t &o)
       {
       {
         width = o.width;
         width = o.width;
         is_signed = 0;
         is_signed = 0;
@@ -158,7 +153,6 @@ struct hb_serialize_context_t
         bias = 0;
         bias = 0;
         objidx = o.objidx;
         objidx = o.objidx;
       }
       }
-#endif
 
 
       HB_INTERNAL static int cmp (const void* a, const void* b)
       HB_INTERNAL static int cmp (const void* a, const void* b)
       {
       {
@@ -178,7 +172,7 @@ struct hb_serialize_context_t
     auto all_links () const HB_AUTO_RETURN
     auto all_links () const HB_AUTO_RETURN
         (( hb_concat (real_links, virtual_links) ));
         (( hb_concat (real_links, virtual_links) ));
     auto all_links_writer () HB_AUTO_RETURN
     auto all_links_writer () HB_AUTO_RETURN
-        (( hb_concat (real_links.writer (), virtual_links.writer ()) ));           
+        (( hb_concat (real_links.writer (), virtual_links.writer ()) ));
   };
   };
 
 
   struct snapshot_t
   struct snapshot_t
@@ -400,6 +394,7 @@ struct hb_serialize_context_t
       {
       {
         merge_virtual_links (obj, objidx);
         merge_virtual_links (obj, objidx);
 	obj->fini ();
 	obj->fini ();
+        object_pool.release (obj);
 	return objidx;
 	return objidx;
       }
       }
     }
     }
@@ -463,9 +458,11 @@ struct hb_serialize_context_t
     while (packed.length > 1 &&
     while (packed.length > 1 &&
 	   packed.tail ()->head < tail)
 	   packed.tail ()->head < tail)
     {
     {
-      packed_map.del (packed.tail ());
-      assert (!packed.tail ()->next);
-      packed.tail ()->fini ();
+      object_t *obj = packed.tail ();
+      packed_map.del (obj);
+      assert (!obj->next);
+      obj->fini ();
+      object_pool.release (obj);
       packed.pop ();
       packed.pop ();
     }
     }
     if (packed.length > 1)
     if (packed.length > 1)

+ 58 - 109
thirdparty/harfbuzz/src/hb-set-digest.hh

@@ -56,7 +56,7 @@
  *   - For each glyph, if it doesn't match the subtable digest,
  *   - For each glyph, if it doesn't match the subtable digest,
  *     skip it.
  *     skip it.
  *
  *
- * The main filter we use is a combination of three bits-pattern
+ * The main filter we use is a combination of four bits-pattern
  * filters. A bits-pattern filter checks a number of bits (5 or 6)
  * filters. A bits-pattern filter checks a number of bits (5 or 6)
  * of the input number (glyph-id in this case) and checks whether
  * of the input number (glyph-id in this case) and checks whether
  * its pattern is amongst the patterns of any of the accepted values.
  * its pattern is amongst the patterns of any of the accepted values.
@@ -64,45 +64,60 @@
  * check is done using four bitwise operations only.
  * check is done using four bitwise operations only.
  */
  */
 
 
-template <typename mask_t, unsigned int shift>
-struct hb_set_digest_bits_pattern_t
+static constexpr unsigned hb_set_digest_shifts[] = {4, 0, 6};
+
+struct hb_set_digest_t
 {
 {
+  // No science in these. Intuition and testing only.
+  using mask_t = uint64_t;
+
+  static constexpr unsigned n = ARRAY_LENGTH_CONST (hb_set_digest_shifts);
   static constexpr unsigned mask_bytes = sizeof (mask_t);
   static constexpr unsigned mask_bytes = sizeof (mask_t);
   static constexpr unsigned mask_bits = sizeof (mask_t) * 8;
   static constexpr unsigned mask_bits = sizeof (mask_t) * 8;
-  static constexpr unsigned num_bits = 0
-				     + (mask_bytes >= 1 ? 3 : 0)
-				     + (mask_bytes >= 2 ? 1 : 0)
-				     + (mask_bytes >= 4 ? 1 : 0)
-				     + (mask_bytes >= 8 ? 1 : 0)
-				     + (mask_bytes >= 16? 1 : 0)
-				     + 0;
+  static constexpr hb_codepoint_t mb1 = mask_bits - 1;
+  static constexpr mask_t one = 1;
+  static constexpr mask_t all = (mask_t) -1;
 
 
-  static_assert ((shift < sizeof (hb_codepoint_t) * 8), "");
-  static_assert ((shift + num_bits <= sizeof (hb_codepoint_t) * 8), "");
-
-  void init () { mask = 0; }
+  void init ()
+  { for (unsigned i = 0; i < n; i++) masks[i] = 0; }
 
 
-  static hb_set_digest_bits_pattern_t full () { hb_set_digest_bits_pattern_t d; d.mask = (mask_t) -1; return d; }
+  void clear () { init (); }
 
 
-  void union_ (const hb_set_digest_bits_pattern_t &o) { mask |= o.mask; }
+  static hb_set_digest_t full ()
+  {
+    hb_set_digest_t d;
+    for (unsigned i = 0; i < n; i++) d.masks[i] = all;
+    return d;
+  }
 
 
-  void add (hb_codepoint_t g) { mask |= mask_for (g); }
+  void union_ (const hb_set_digest_t &o)
+  { for (unsigned i = 0; i < n; i++) masks[i] |= o.masks[i]; }
 
 
   bool add_range (hb_codepoint_t a, hb_codepoint_t b)
   bool add_range (hb_codepoint_t a, hb_codepoint_t b)
   {
   {
-    if (mask == (mask_t) -1) return false;
-    if ((b >> shift) - (a >> shift) >= mask_bits - 1)
-    {
-      mask = (mask_t) -1;
-      return false;
-    }
-    else
+    bool ret;
+
+    ret = false;
+    for (unsigned i = 0; i < n; i++)
+      if (masks[i] != all)
+	ret = true;
+    if (!ret) return false;
+
+    ret = false;
+    for (unsigned i = 0; i < n; i++)
     {
     {
-      mask_t ma = mask_for (a);
-      mask_t mb = mask_for (b);
-      mask |= mb + (mb - ma) - (mb < ma);
-      return true;
+      mask_t shift = hb_set_digest_shifts[i];
+      if ((b >> shift) - (a >> shift) >= mb1)
+	masks[i] = all;
+      else
+      {
+	mask_t ma = one << ((a >> shift) & mb1);
+	mask_t mb = one << ((b >> shift) & mb1);
+	masks[i] |= mb + (mb - ma) - (mb < ma);
+	ret = true;
+      }
     }
     }
+    return ret;
   }
   }
 
 
   template <typename T>
   template <typename T>
@@ -125,103 +140,37 @@ struct hb_set_digest_bits_pattern_t
   template <typename T>
   template <typename T>
   bool add_sorted_array (const hb_sorted_array_t<const T>& arr) { return add_sorted_array (&arr, arr.len ()); }
   bool add_sorted_array (const hb_sorted_array_t<const T>& arr) { return add_sorted_array (&arr, arr.len ()); }
 
 
-  bool may_have (const hb_set_digest_bits_pattern_t &o) const
-  { return mask & o.mask; }
-
-  bool may_have (hb_codepoint_t g) const
-  { return mask & mask_for (g); }
-
   bool operator [] (hb_codepoint_t g) const
   bool operator [] (hb_codepoint_t g) const
   { return may_have (g); }
   { return may_have (g); }
 
 
-  private:
-
-  static mask_t mask_for (hb_codepoint_t g)
-  { return ((mask_t) 1) << ((g >> shift) & (mask_bits - 1)); }
-  mask_t mask = 0;
-};
-
-template <typename head_t, typename tail_t>
-struct hb_set_digest_combiner_t
-{
-  void init ()
-  {
-    head.init ();
-    tail.init ();
-  }
-
-  static hb_set_digest_combiner_t full () { hb_set_digest_combiner_t d; d.head = head_t::full(); d.tail = tail_t::full (); return d; }
-
-  void union_ (const hb_set_digest_combiner_t &o)
-  {
-    head.union_ (o.head);
-    tail.union_(o.tail);
-  }
 
 
   void add (hb_codepoint_t g)
   void add (hb_codepoint_t g)
   {
   {
-    head.add (g);
-    tail.add (g);
-  }
-
-  bool add_range (hb_codepoint_t a, hb_codepoint_t b)
-  {
-    return (int) head.add_range (a, b) | (int) tail.add_range (a, b);
-  }
-  template <typename T>
-  void add_array (const T *array, unsigned int count, unsigned int stride=sizeof(T))
-  {
-    head.add_array (array, count, stride);
-    tail.add_array (array, count, stride);
-  }
-  template <typename T>
-  void add_array (const hb_array_t<const T>& arr) { add_array (&arr, arr.len ()); }
-  template <typename T>
-  bool add_sorted_array (const T *array, unsigned int count, unsigned int stride=sizeof(T))
-  {
-    return head.add_sorted_array (array, count, stride) &&
-	   tail.add_sorted_array (array, count, stride);
+    for (unsigned i = 0; i < n; i++)
+      masks[i] |= one << ((g >> hb_set_digest_shifts[i]) & mb1);
   }
   }
-  template <typename T>
-  bool add_sorted_array (const hb_sorted_array_t<const T>& arr) { return add_sorted_array (&arr, arr.len ()); }
 
 
-  bool may_have (const hb_set_digest_combiner_t &o) const
+  HB_ALWAYS_INLINE
+  bool may_have (hb_codepoint_t g) const
   {
   {
-    return head.may_have (o.head) && tail.may_have (o.tail);
+    for (unsigned i = 0; i < n; i++)
+      if (!(masks[i] & (one << ((g >> hb_set_digest_shifts[i]) & mb1))))
+	return false;
+    return true;
   }
   }
 
 
-  bool may_have (hb_codepoint_t g) const
+  bool may_intersect (const hb_set_digest_t &o) const
   {
   {
-    return head.may_have (g) && tail.may_have (g);
+    for (unsigned i = 0; i < n; i++)
+      if (!(masks[i] & o.masks[i]))
+	return false;
+    return true;
   }
   }
 
 
-  bool operator [] (hb_codepoint_t g) const
-  { return may_have (g); }
-
   private:
   private:
-  head_t head;
-  tail_t tail;
-};
-
 
 
-/*
- * hb_set_digest_t
- *
- * This is a combination of digests that performs "best".
- * There is not much science to this: it's a result of intuition
- * and testing.
- */
-using hb_set_digest_t =
-  hb_set_digest_combiner_t
-  <
-    hb_set_digest_bits_pattern_t<unsigned long, 4>,
-    hb_set_digest_combiner_t
-    <
-      hb_set_digest_bits_pattern_t<unsigned long, 0>,
-      hb_set_digest_bits_pattern_t<unsigned long, 9>
-    >
-  >
-;
+  mask_t masks[n] = {};
+};
 
 
 
 
 #endif /* HB_SET_DIGEST_HH */
 #endif /* HB_SET_DIGEST_HH */

+ 4 - 0
thirdparty/harfbuzz/src/hb-set.hh

@@ -106,6 +106,7 @@ struct hb_sparseset_t
   void del_range (hb_codepoint_t a, hb_codepoint_t b) { s.del_range (a, b); }
   void del_range (hb_codepoint_t a, hb_codepoint_t b) { s.del_range (a, b); }
 
 
   bool get (hb_codepoint_t g) const { return s.get (g); }
   bool get (hb_codepoint_t g) const { return s.get (g); }
+  bool may_have (hb_codepoint_t g) const { return get (g); }
 
 
   /* Has interface. */
   /* Has interface. */
   bool operator [] (hb_codepoint_t k) const { return get (k); }
   bool operator [] (hb_codepoint_t k) const { return get (k); }
@@ -120,6 +121,9 @@ struct hb_sparseset_t
   hb_sparseset_t& operator << (const hb_codepoint_pair_t& range)
   hb_sparseset_t& operator << (const hb_codepoint_pair_t& range)
   { add_range (range.first, range.second); return *this; }
   { add_range (range.first, range.second); return *this; }
 
 
+  bool may_intersect (const hb_sparseset_t &other) const
+  { return s.may_intersect (other.s); }
+
   bool intersects (hb_codepoint_t first, hb_codepoint_t last) const
   bool intersects (hb_codepoint_t first, hb_codepoint_t last) const
   { return s.intersects (first, last); }
   { return s.intersects (first, last); }
 
 

+ 5 - 5
thirdparty/harfbuzz/src/hb-shape-plan.cc

@@ -209,7 +209,7 @@ hb_shape_plan_create (hb_face_t                     *face,
  * @num_coords: The number of variation-space coordinates
  * @num_coords: The number of variation-space coordinates
  * @shaper_list: (array zero-terminated=1): List of shapers to try
  * @shaper_list: (array zero-terminated=1): List of shapers to try
  *
  *
- * The variable-font version of #hb_shape_plan_create. 
+ * The variable-font version of #hb_shape_plan_create.
  * Constructs a shaping plan for a combination of @face, @user_features, @props,
  * Constructs a shaping plan for a combination of @face, @user_features, @props,
  * and @shaper_list, plus the variation-space coordinates @coords.
  * and @shaper_list, plus the variation-space coordinates @coords.
  *
  *
@@ -233,7 +233,7 @@ hb_shape_plan_create2 (hb_face_t                     *face,
 		  num_coords,
 		  num_coords,
 		  shaper_list);
 		  shaper_list);
 
 
-  if (unlikely (props->direction == HB_DIRECTION_INVALID))
+  if (unlikely (!HB_DIRECTION_IS_VALID (props->direction)))
     return hb_shape_plan_get_empty ();
     return hb_shape_plan_get_empty ();
 
 
   hb_shape_plan_t *shape_plan;
   hb_shape_plan_t *shape_plan;
@@ -331,7 +331,7 @@ hb_shape_plan_destroy (hb_shape_plan_t *shape_plan)
  * @destroy: (nullable): A callback to call when @data is not needed anymore
  * @destroy: (nullable): A callback to call when @data is not needed anymore
  * @replace: Whether to replace an existing data with the same key
  * @replace: Whether to replace an existing data with the same key
  *
  *
- * Attaches a user-data key/data pair to the given shaping plan. 
+ * Attaches a user-data key/data pair to the given shaping plan.
  *
  *
  * Return value: `true` if success, `false` otherwise.
  * Return value: `true` if success, `false` otherwise.
  *
  *
@@ -352,7 +352,7 @@ hb_shape_plan_set_user_data (hb_shape_plan_t    *shape_plan,
  * @shape_plan: A shaping plan
  * @shape_plan: A shaping plan
  * @key: The user-data key to query
  * @key: The user-data key to query
  *
  *
- * Fetches the user data associated with the specified key, 
+ * Fetches the user data associated with the specified key,
  * attached to the specified shaping plan.
  * attached to the specified shaping plan.
  *
  *
  * Return value: (transfer none): A pointer to the user data
  * Return value: (transfer none): A pointer to the user data
@@ -501,7 +501,7 @@ hb_shape_plan_create_cached (hb_face_t                     *face,
  * @num_coords: The number of variation-space coordinates
  * @num_coords: The number of variation-space coordinates
  * @shaper_list: (array zero-terminated=1): List of shapers to try
  * @shaper_list: (array zero-terminated=1): List of shapers to try
  *
  *
- * The variable-font version of #hb_shape_plan_create_cached. 
+ * The variable-font version of #hb_shape_plan_create_cached.
  * Creates a cached shaping plan suitable for reuse, for a combination
  * Creates a cached shaping plan suitable for reuse, for a combination
  * of @face, @user_features, @props, and @shaper_list, plus the
  * of @face, @user_features, @props, and @shaper_list, plus the
  * variation-space coordinates @coords.
  * variation-space coordinates @coords.

部分文件因为文件数量过多而无法显示