Procházet zdrojové kódy

Merge pull request #108859 from bruvzg/hb1132

harfbuzz: Update to 11.3.2
Thaddeus Crews před 3 týdny
rodič
revize
cb9ec1b767
72 změnil soubory, kde provedl 3844 přidání a 2316 odebrání
  1. 2 2
      thirdparty/README.md
  2. 21 36
      thirdparty/harfbuzz/src/OT/Color/COLR/COLR.hh
  3. 10 7
      thirdparty/harfbuzz/src/OT/Layout/GPOS/AnchorFormat3.hh
  4. 3 3
      thirdparty/harfbuzz/src/OT/Layout/GPOS/ValueFormat.hh
  5. 2 2
      thirdparty/harfbuzz/src/OT/Layout/GSUB/LigatureSet.hh
  6. 1 1
      thirdparty/harfbuzz/src/OT/Layout/GSUB/Sequence.hh
  7. 28 28
      thirdparty/harfbuzz/src/OT/Var/VARC/VARC.cc
  8. 9 9
      thirdparty/harfbuzz/src/OT/Var/VARC/VARC.hh
  9. 19 19
      thirdparty/harfbuzz/src/OT/glyf/Glyph.hh
  10. 80 66
      thirdparty/harfbuzz/src/OT/glyf/glyf.hh
  11. 1 1
      thirdparty/harfbuzz/src/graph/classdef-graph.hh
  12. 23 1
      thirdparty/harfbuzz/src/graph/coverage-graph.hh
  13. 64 19
      thirdparty/harfbuzz/src/graph/graph.hh
  14. 50 24
      thirdparty/harfbuzz/src/graph/gsubgpos-graph.hh
  15. 480 0
      thirdparty/harfbuzz/src/graph/ligature-graph.hh
  16. 1 1
      thirdparty/harfbuzz/src/graph/pairpos-graph.hh
  17. 10 6
      thirdparty/harfbuzz/src/graph/serialize.hh
  18. 183 70
      thirdparty/harfbuzz/src/hb-algs.hh
  19. 4 3
      thirdparty/harfbuzz/src/hb-cache.hh
  20. 1 1
      thirdparty/harfbuzz/src/hb-cff2-interp-cs.hh
  21. 5 2
      thirdparty/harfbuzz/src/hb-common.cc
  22. 4 7
      thirdparty/harfbuzz/src/hb-coretext-shape.cc
  23. 2 1
      thirdparty/harfbuzz/src/hb-coretext.cc
  24. 4 4
      thirdparty/harfbuzz/src/hb-deprecated.h
  25. 55 0
      thirdparty/harfbuzz/src/hb-directwrite.cc
  26. 11 11
      thirdparty/harfbuzz/src/hb-draw.cc
  27. 1 2
      thirdparty/harfbuzz/src/hb-face-builder.cc
  28. 1 1
      thirdparty/harfbuzz/src/hb-face.cc
  29. 211 37
      thirdparty/harfbuzz/src/hb-font.cc
  30. 120 9
      thirdparty/harfbuzz/src/hb-font.h
  31. 300 57
      thirdparty/harfbuzz/src/hb-font.hh
  32. 9 21
      thirdparty/harfbuzz/src/hb-ft-colr.hh
  33. 25 5
      thirdparty/harfbuzz/src/hb-ft.cc
  34. 158 89
      thirdparty/harfbuzz/src/hb-geometry.hh
  35. 0 8
      thirdparty/harfbuzz/src/hb-glib.cc
  36. 0 6
      thirdparty/harfbuzz/src/hb-gobject-structs.cc
  37. 9 8
      thirdparty/harfbuzz/src/hb-machinery.hh
  38. 72 30
      thirdparty/harfbuzz/src/hb-open-type.hh
  39. 5 1
      thirdparty/harfbuzz/src/hb-ot-cff2-table.cc
  40. 1 5
      thirdparty/harfbuzz/src/hb-ot-cmap-table.hh
  41. 477 79
      thirdparty/harfbuzz/src/hb-ot-font.cc
  42. 16 63
      thirdparty/harfbuzz/src/hb-ot-hmtx-table.hh
  43. 207 123
      thirdparty/harfbuzz/src/hb-ot-layout-common.hh
  44. 2 2
      thirdparty/harfbuzz/src/hb-ot-layout-gsubgpos.hh
  45. 26 26
      thirdparty/harfbuzz/src/hb-ot-layout.cc
  46. 16 90
      thirdparty/harfbuzz/src/hb-ot-shape.cc
  47. 1 1
      thirdparty/harfbuzz/src/hb-ot-shaper-arabic.cc
  48. 1 1
      thirdparty/harfbuzz/src/hb-ot-shaper-thai.cc
  49. 16 20
      thirdparty/harfbuzz/src/hb-ot-shaper-use-table.hh
  50. 2 2
      thirdparty/harfbuzz/src/hb-ot-tag-table.hh
  51. 94 47
      thirdparty/harfbuzz/src/hb-ot-var-avar-table.hh
  52. 63 52
      thirdparty/harfbuzz/src/hb-ot-var-common.hh
  53. 6 23
      thirdparty/harfbuzz/src/hb-ot-var-fvar-table.hh
  54. 19 35
      thirdparty/harfbuzz/src/hb-ot-var-gvar-table.hh
  55. 13 18
      thirdparty/harfbuzz/src/hb-ot-var-hvar-table.hh
  56. 19 7
      thirdparty/harfbuzz/src/hb-ot-var.cc
  57. 1 0
      thirdparty/harfbuzz/src/hb-ot-vorg-table.hh
  58. 7 7
      thirdparty/harfbuzz/src/hb-paint-extents.cc
  59. 19 19
      thirdparty/harfbuzz/src/hb-paint-extents.hh
  60. 42 32
      thirdparty/harfbuzz/src/hb-paint.hh
  61. 7 4
      thirdparty/harfbuzz/src/hb-repacker.hh
  62. 18 8
      thirdparty/harfbuzz/src/hb-sanitize.hh
  63. 2 1
      thirdparty/harfbuzz/src/hb-serialize.hh
  64. 2 2
      thirdparty/harfbuzz/src/hb-shaper-list.hh
  65. 0 23
      thirdparty/harfbuzz/src/hb-static.cc
  66. 62 39
      thirdparty/harfbuzz/src/hb-subset-plan-var.cc
  67. 2 2
      thirdparty/harfbuzz/src/hb-subset-plan.cc
  68. 652 973
      thirdparty/harfbuzz/src/hb-ucd-table.hh
  69. 9 11
      thirdparty/harfbuzz/src/hb-unicode-emoji-table.hh
  70. 51 0
      thirdparty/harfbuzz/src/hb-unicode.hh
  71. 3 3
      thirdparty/harfbuzz/src/hb-version.h
  72. 4 0
      thirdparty/harfbuzz/src/hb.hh

+ 2 - 2
thirdparty/README.md

@@ -436,7 +436,7 @@ Patches:
 ## harfbuzz
 
 - Upstream: https://github.com/harfbuzz/harfbuzz
-- Version: 11.2.1 (33a3f8de60dcad7535f14f07d6710144548853ac, 2025)
+- Version: 11.3.2 (4e3df1c1383481ed5717603d5dd3453a04fb16ba, 2025)
 - License: MIT
 
 Files extracted from upstream source:
@@ -444,7 +444,7 @@ Files extracted from upstream source:
 - `AUTHORS`, `COPYING`, `THANKS`
 - From the `src` folder, recursively:
   - All the `.cc`, `.h`, `.hh` files
-  - Except `main.cc`, `harfbuzz*.cc`, `failing-alloc.c`, `test*.cc`, `hb-wasm*.*`, `wasm/*`
+  - Except `main.cc`, `harfbuzz*.cc`, `failing-alloc.c`, `test*.cc`, `hb-wasm*.*`, `hb-harfrust.cc`, `wasm/*`, `ms-use/*`, `rust/*`
 
 
 ## hidapi

+ 21 - 36
thirdparty/harfbuzz/src/OT/Color/COLR/COLR.hh

@@ -104,7 +104,7 @@ public:
     foreground (foreground_),
     instancer (instancer_)
   {
-    if (font->is_synthetic ())
+    if (font->is_synthetic)
     {
       font = hb_font_create_sub_font (font);
       hb_font_set_synthetic_bold (font, 0, 0, true);
@@ -1075,9 +1075,9 @@ struct PaintTranslate
     float ddx = dx + c->instancer (varIdxBase, 0);
     float ddy = dy + c->instancer (varIdxBase, 1);
 
-    bool p1 = c->funcs->push_translate (c->data, ddx, ddy);
+    c->funcs->push_translate (c->data, ddx, ddy);
     c->recurse (this+src);
-    if (p1) c->funcs->pop_transform (c->data);
+    c->funcs->pop_transform (c->data);
   }
 
   HBUINT8		format; /* format = 14(noVar) or 15 (Var) */
@@ -1124,9 +1124,9 @@ struct PaintScale
     float sx = scaleX.to_float (c->instancer (varIdxBase, 0));
     float sy = scaleY.to_float (c->instancer (varIdxBase, 1));
 
-    bool p1 = c->funcs->push_scale (c->data, sx, sy);
+    c->funcs->push_scale (c->data, sx, sy);
     c->recurse (this+src);
-    if (p1) c->funcs->pop_transform (c->data);
+    c->funcs->pop_transform (c->data);
   }
 
   HBUINT8		format; /* format = 16 (noVar) or 17(Var) */
@@ -1177,13 +1177,9 @@ struct PaintScaleAroundCenter
     float tCenterX = centerX + c->instancer (varIdxBase, 2);
     float tCenterY = centerY + c->instancer (varIdxBase, 3);
 
-    bool p1 = c->funcs->push_translate (c->data, +tCenterX, +tCenterY);
-    bool p2 = c->funcs->push_scale (c->data, sx, sy);
-    bool p3 = c->funcs->push_translate (c->data, -tCenterX, -tCenterY);
+    c->funcs->push_scale_around_center (c->data, sx, sy, tCenterX, tCenterY);
     c->recurse (this+src);
-    if (p3) c->funcs->pop_transform (c->data);
-    if (p2) c->funcs->pop_transform (c->data);
-    if (p1) c->funcs->pop_transform (c->data);
+    c->funcs->pop_transform (c->data);
   }
 
   HBUINT8		format; /* format = 18 (noVar) or 19(Var) */
@@ -1228,9 +1224,9 @@ struct PaintScaleUniform
     TRACE_PAINT (this);
     float s = scale.to_float (c->instancer (varIdxBase, 0));
 
-    bool p1 = c->funcs->push_scale (c->data, s, s);
+    c->funcs->push_scale (c->data, s, s);
     c->recurse (this+src);
-    if (p1) c->funcs->pop_transform (c->data);
+    c->funcs->pop_transform (c->data);
   }
 
   HBUINT8		format; /* format = 20 (noVar) or 21(Var) */
@@ -1278,13 +1274,9 @@ struct PaintScaleUniformAroundCenter
     float tCenterX = centerX + c->instancer (varIdxBase, 1);
     float tCenterY = centerY + c->instancer (varIdxBase, 2);
 
-    bool p1 = c->funcs->push_translate (c->data, +tCenterX, +tCenterY);
-    bool p2 = c->funcs->push_scale (c->data, s, s);
-    bool p3 = c->funcs->push_translate (c->data, -tCenterX, -tCenterY);
+    c->funcs->push_scale_around_center (c->data, s, s, tCenterX, tCenterY);
     c->recurse (this+src);
-    if (p3) c->funcs->pop_transform (c->data);
-    if (p2) c->funcs->pop_transform (c->data);
-    if (p1) c->funcs->pop_transform (c->data);
+    c->funcs->pop_transform (c->data);
   }
 
   HBUINT8		format; /* format = 22 (noVar) or 23(Var) */
@@ -1328,9 +1320,9 @@ struct PaintRotate
     TRACE_PAINT (this);
     float a = angle.to_float (c->instancer (varIdxBase, 0));
 
-    bool p1 = c->funcs->push_rotate (c->data, a);
+    c->funcs->push_rotate (c->data, a);
     c->recurse (this+src);
-    if (p1) c->funcs->pop_transform (c->data);
+    c->funcs->pop_transform (c->data);
   }
 
   HBUINT8		format; /* format = 24 (noVar) or 25(Var) */
@@ -1378,13 +1370,9 @@ struct PaintRotateAroundCenter
     float tCenterX = centerX + c->instancer (varIdxBase, 1);
     float tCenterY = centerY + c->instancer (varIdxBase, 2);
 
-    bool p1 = c->funcs->push_translate (c->data, +tCenterX, +tCenterY);
-    bool p2 = c->funcs->push_rotate (c->data, a);
-    bool p3 = c->funcs->push_translate (c->data, -tCenterX, -tCenterY);
+    c->funcs->push_rotate_around_center (c->data, a, tCenterX, tCenterY);
     c->recurse (this+src);
-    if (p3) c->funcs->pop_transform (c->data);
-    if (p2) c->funcs->pop_transform (c->data);
-    if (p1) c->funcs->pop_transform (c->data);
+    c->funcs->pop_transform (c->data);
   }
 
   HBUINT8		format; /* format = 26 (noVar) or 27(Var) */
@@ -1432,9 +1420,9 @@ struct PaintSkew
     float sx = xSkewAngle.to_float(c->instancer (varIdxBase, 0));
     float sy = ySkewAngle.to_float(c->instancer (varIdxBase, 1));
 
-    bool p1 = c->funcs->push_skew (c->data, sx, sy);
+    c->funcs->push_skew (c->data, sx, sy);
     c->recurse (this+src);
-    if (p1) c->funcs->pop_transform (c->data);
+    c->funcs->pop_transform (c->data);
   }
 
   HBUINT8		format; /* format = 28(noVar) or 29 (Var) */
@@ -1485,13 +1473,9 @@ struct PaintSkewAroundCenter
     float tCenterX = centerX + c->instancer (varIdxBase, 2);
     float tCenterY = centerY + c->instancer (varIdxBase, 3);
 
-    bool p1 = c->funcs->push_translate (c->data, +tCenterX, +tCenterY);
-    bool p2 = c->funcs->push_skew (c->data, sx, sy);
-    bool p3 = c->funcs->push_translate (c->data, -tCenterX, -tCenterY);
+    c->funcs->push_skew_around_center (c->data, sx, sy, tCenterX, tCenterY);
     c->recurse (this+src);
-    if (p3) c->funcs->pop_transform (c->data);
-    if (p2) c->funcs->pop_transform (c->data);
-    if (p1) c->funcs->pop_transform (c->data);
+    c->funcs->pop_transform (c->data);
   }
 
   HBUINT8		format; /* format = 30(noVar) or 31 (Var) */
@@ -2693,7 +2677,8 @@ struct COLR
   {
     ItemVarStoreInstancer instancer (get_var_store_ptr (),
 				     get_delta_set_index_map_ptr (),
-				     hb_array (font->coords, font->num_coords));
+				     hb_array (font->coords,
+					       font->has_nonzero_coords ? font->num_coords : 0));
     hb_paint_context_t c (this, funcs, data, font, palette_index, foreground, instancer);
 
     hb_decycler_node_t node (c.glyphs_decycler);

+ 10 - 7
thirdparty/harfbuzz/src/OT/Layout/GPOS/AnchorFormat3.hh

@@ -37,12 +37,12 @@ struct AnchorFormat3
     *x = font->em_fscale_x (xCoordinate);
     *y = font->em_fscale_y (yCoordinate);
 
-    if ((font->x_ppem || font->num_coords) && xDeviceTable.sanitize (&c->sanitizer, this))
+    if ((font->x_ppem || font->has_nonzero_coords) && xDeviceTable.sanitize (&c->sanitizer, this))
     {
       hb_barrier ();
       *x += (this+xDeviceTable).get_x_delta (font, c->var_store, c->var_store_cache);
     }
-    if ((font->y_ppem || font->num_coords) && yDeviceTable.sanitize (&c->sanitizer, this))
+    if ((font->y_ppem || font->has_nonzero_coords) && yDeviceTable.sanitize (&c->sanitizer, this))
     {
       hb_barrier ();
       *y += (this+yDeviceTable).get_y_delta (font, c->var_store, c->var_store_cache);
@@ -63,7 +63,7 @@ struct AnchorFormat3
       hb_pair_t<unsigned, int> *new_varidx_delta;
       if (!c->plan->layout_variation_idx_delta_map.has (x_varidx, &new_varidx_delta))
         return_trace (false);
-     
+
       x_varidx = hb_first (*new_varidx_delta);
       int delta = hb_second (*new_varidx_delta);
       if (delta != 0)
@@ -91,10 +91,13 @@ struct AnchorFormat3
       }
     }
 
-    /* in case that all axes are pinned or no variations after instantiation,
-     * both var_idxes will be mapped to HB_OT_LAYOUT_NO_VARIATIONS_INDEX */
-    if (x_varidx == HB_OT_LAYOUT_NO_VARIATIONS_INDEX &&
-        y_varidx == HB_OT_LAYOUT_NO_VARIATIONS_INDEX)
+
+    bool no_downgrade = (!xDeviceTable.is_null () && !(this+xDeviceTable).is_variation_device ()) ||
+                        x_varidx != HB_OT_LAYOUT_NO_VARIATIONS_INDEX ||
+                        y_varidx != HB_OT_LAYOUT_NO_VARIATIONS_INDEX ||
+                        (!yDeviceTable.is_null () && !(this+yDeviceTable).is_variation_device ());
+
+    if (!no_downgrade)
       return_trace (c->serializer->check_assign (out->format, 1, HB_SERIALIZE_ERROR_INT_OVERFLOW));
 
     if (!c->serializer->embed (xDeviceTable)) return_trace (false);

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

@@ -56,7 +56,7 @@ struct ValueFormat : HBUINT16
                                          * PosTable (may be NULL) */
 #endif
 
-  IntType& operator = (uint16_t i) { v = i; return *this; }
+  NumType& operator = (uint16_t i) { v = i; return *this; }
 
   unsigned int get_len () const  { return hb_popcount ((unsigned int) *this); }
   unsigned int get_size () const { return get_len () * Value::static_size; }
@@ -111,8 +111,8 @@ struct ValueFormat : HBUINT16
 
     if (!has_device ()) return ret;
 
-    bool use_x_device = font->x_ppem || font->num_coords;
-    bool use_y_device = font->y_ppem || font->num_coords;
+    bool use_x_device = font->x_ppem || font->has_nonzero_coords;
+    bool use_y_device = font->y_ppem || font->has_nonzero_coords;
 
     if (!use_x_device && !use_y_device) return ret;
 

+ 2 - 2
thirdparty/harfbuzz/src/OT/Layout/GSUB/LigatureSet.hh

@@ -11,11 +11,11 @@ namespace GSUB_impl {
 template <typename Types>
 struct LigatureSet
 {
-  protected:
+  public:
   Array16OfOffset16To<Ligature<Types>>
                 ligature;               /* Array LigatureSet tables
                                          * ordered by preference */
-  public:
+  
   DEFINE_SIZE_ARRAY (2, ligature);
 
   bool sanitize (hb_sanitize_context_t *c) const

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

@@ -115,7 +115,7 @@ struct Sequence
 
       for (unsigned i = c->buffer->idx - count; i < c->buffer->idx; i++)
       {
-	if (buf < p)
+	if (buf < p && sizeof(buf) - 1u > unsigned (p - buf))
 	  *p++ = ',';
 	snprintf (p, sizeof(buf) - (p - buf), "%u", i);
 	p += strlen(p);

+ 28 - 28
thirdparty/harfbuzz/src/OT/Var/VARC/VARC.cc

@@ -13,7 +13,7 @@ namespace OT {
 
 struct hb_transforming_pen_context_t
 {
-  hb_transform_t transform;
+  hb_transform_t<> transform;
   hb_draw_funcs_t *dfuncs;
   void *data;
   hb_draw_state_t *st;
@@ -130,9 +130,9 @@ hb_ubytes_t
 VarComponent::get_path_at (const hb_varc_context_t &c,
 			   hb_codepoint_t parent_gid,
 			   hb_array_t<const int> coords,
-			   hb_transform_t total_transform,
+			   hb_transform_t<> total_transform,
 			   hb_ubytes_t total_record,
-			   VarRegionList::cache_t *cache) const
+			   hb_scalar_cache_t *cache) const
 {
   const unsigned char *end = total_record.arrayZ + total_record.length;
   const unsigned char *record = total_record.arrayZ;
@@ -216,7 +216,7 @@ VarComponent::get_path_at (const hb_varc_context_t &c,
    * limit on the max number of coords for now. */
   if ((flags & (unsigned) flags_t::RESET_UNSPECIFIED_AXES) ||
       coords.length > HB_VAR_COMPOSITE_MAX_AXES)
-    component_coords = hb_array<int> (c.font->coords, c.font->num_coords);
+    component_coords = hb_array (c.font->coords, c.font->num_coords);
 
   // Transform
 
@@ -226,28 +226,28 @@ VarComponent::get_path_at (const hb_varc_context_t &c,
 
 #define PROCESS_TRANSFORM_COMPONENTS \
 	HB_STMT_START { \
-	PROCESS_TRANSFORM_COMPONENT (FWORD, HAVE_TRANSLATE_X, translateX); \
-	PROCESS_TRANSFORM_COMPONENT (FWORD, HAVE_TRANSLATE_Y, translateY); \
-	PROCESS_TRANSFORM_COMPONENT (F4DOT12, HAVE_ROTATION, rotation); \
-	PROCESS_TRANSFORM_COMPONENT (F6DOT10, HAVE_SCALE_X, scaleX); \
-	PROCESS_TRANSFORM_COMPONENT (F6DOT10, HAVE_SCALE_Y, scaleY); \
-	PROCESS_TRANSFORM_COMPONENT (F4DOT12, HAVE_SKEW_X, skewX); \
-	PROCESS_TRANSFORM_COMPONENT (F4DOT12, HAVE_SKEW_Y, skewY); \
-	PROCESS_TRANSFORM_COMPONENT (FWORD, HAVE_TCENTER_X, tCenterX); \
-	PROCESS_TRANSFORM_COMPONENT (FWORD, HAVE_TCENTER_Y, tCenterY); \
+	PROCESS_TRANSFORM_COMPONENT (FWORD, 1.0f, HAVE_TRANSLATE_X, translateX); \
+	PROCESS_TRANSFORM_COMPONENT (FWORD, 1.0f, HAVE_TRANSLATE_Y, translateY); \
+	PROCESS_TRANSFORM_COMPONENT (F4DOT12, HB_PI, HAVE_ROTATION, rotation); \
+	PROCESS_TRANSFORM_COMPONENT (F6DOT10, 1.0f, HAVE_SCALE_X, scaleX); \
+	PROCESS_TRANSFORM_COMPONENT (F6DOT10, 1.0f, HAVE_SCALE_Y, scaleY); \
+	PROCESS_TRANSFORM_COMPONENT (F4DOT12, HB_PI, HAVE_SKEW_X, skewX); \
+	PROCESS_TRANSFORM_COMPONENT (F4DOT12, HB_PI, HAVE_SKEW_Y, skewY); \
+	PROCESS_TRANSFORM_COMPONENT (FWORD, 1.0f, HAVE_TCENTER_X, tCenterX); \
+	PROCESS_TRANSFORM_COMPONENT (FWORD, 1.0f, HAVE_TCENTER_Y, tCenterY); \
 	} HB_STMT_END
 
-  hb_transform_decomposed_t transform;
+  hb_transform_decomposed_t<> transform;
 
   // Read transform components
-#define PROCESS_TRANSFORM_COMPONENT(type, flag, name) \
+#define PROCESS_TRANSFORM_COMPONENT(type, mult, flag, name) \
 	if (flags & (unsigned) flags_t::flag) \
 	{ \
 	  static_assert (type::static_size == HBINT16::static_size, ""); \
 	  if (unlikely (unsigned (end - record) < HBINT16::static_size)) \
 	    return hb_ubytes_t (); \
 	  hb_barrier (); \
-	  transform.name = * (const HBINT16 *) record; \
+	  transform.name = mult * * (const HBINT16 *) record; \
 	  record += HBINT16::static_size; \
 	}
   PROCESS_TRANSFORM_COMPONENTS;
@@ -279,22 +279,22 @@ VarComponent::get_path_at (const hb_varc_context_t &c,
     {
       float transformValues[9];
       unsigned numTransformValues = 0;
-#define PROCESS_TRANSFORM_COMPONENT(type, flag, name) \
+#define PROCESS_TRANSFORM_COMPONENT(type, mult, flag, name) \
 	  if (flags & (unsigned) flags_t::flag) \
-	    transformValues[numTransformValues++] = transform.name;
+	    transformValues[numTransformValues++] = transform.name / mult;
       PROCESS_TRANSFORM_COMPONENTS;
 #undef PROCESS_TRANSFORM_COMPONENT
       varStore.get_delta (transformVarIdx, coords, hb_array (transformValues, numTransformValues), cache);
       numTransformValues = 0;
-#define PROCESS_TRANSFORM_COMPONENT(type, flag, name) \
+#define PROCESS_TRANSFORM_COMPONENT(type, mult, flag, name) \
 	  if (flags & (unsigned) flags_t::flag) \
-	    transform.name = transformValues[numTransformValues++];
+	    transform.name = transformValues[numTransformValues++] * mult;
       PROCESS_TRANSFORM_COMPONENTS;
 #undef PROCESS_TRANSFORM_COMPONENT
     }
 
     // Divide them by their divisors
-#define PROCESS_TRANSFORM_COMPONENT(type, flag, name) \
+#define PROCESS_TRANSFORM_COMPONENT(type, mult, flag, name) \
 	  if (flags & (unsigned) flags_t::flag) \
 	  { \
 	    HBINT16 int_v; \
@@ -334,9 +334,9 @@ bool
 VARC::get_path_at (const hb_varc_context_t &c,
 		   hb_codepoint_t glyph,
 		   hb_array_t<const int> coords,
-		   hb_transform_t transform,
+		   hb_transform_t<> transform,
 		   hb_codepoint_t parent_glyph,
-		   VarRegionList::cache_t *parent_cache) const
+		   hb_scalar_cache_t *parent_cache) const
 {
   // Don't recurse on the same glyph.
   unsigned idx = glyph == parent_glyph ?
@@ -372,7 +372,7 @@ VARC::get_path_at (const hb_varc_context_t &c,
 #endif
 	return false;
 
-      hb_extents_t comp_extents (glyph_extents);
+      hb_extents_t<> comp_extents (glyph_extents);
       transform.transform_extents (comp_extents);
       c.extents->union_ (comp_extents);
     }
@@ -392,10 +392,10 @@ VARC::get_path_at (const hb_varc_context_t &c,
 
   hb_ubytes_t record = (this+glyphRecords)[idx];
 
-  VarRegionList::cache_t static_cache[sizeof (void *) * 16];
-  VarRegionList::cache_t *cache = parent_cache ?
+  hb_scalar_cache_t static_cache;
+  hb_scalar_cache_t *cache = parent_cache ?
 				  parent_cache :
-				  (this+varStore).create_cache (hb_array (static_cache));
+				  (this+varStore).create_cache (&static_cache);
 
   transform.scale (c.font->x_multf, c.font->y_multf);
 
@@ -406,7 +406,7 @@ VARC::get_path_at (const hb_varc_context_t &c,
 				  cache);
 
   if (cache != parent_cache)
-    (this+varStore).destroy_cache (cache, hb_array (static_cache));
+    (this+varStore).destroy_cache (cache, &static_cache);
 
   return true;
 }

+ 9 - 9
thirdparty/harfbuzz/src/OT/Var/VARC/VARC.hh

@@ -32,7 +32,7 @@ struct hb_varc_context_t
 {
   hb_font_t *font;
   hb_draw_session_t *draw_session;
-  hb_extents_t *extents;
+  hb_extents_t<> *extents;
   mutable hb_decycler_t decycler;
   mutable signed edges_left;
   mutable signed depth_left;
@@ -65,9 +65,9 @@ struct VarComponent
   get_path_at (const hb_varc_context_t &c,
 	       hb_codepoint_t parent_gid,
 	       hb_array_t<const int> coords,
-	       hb_transform_t transform,
+	       hb_transform_t<> transform,
 	       hb_ubytes_t record,
-	       VarRegionList::cache_t *cache = nullptr) const;
+	       hb_scalar_cache_t *cache = nullptr) const;
 };
 
 struct VarCompositeGlyph
@@ -76,9 +76,9 @@ struct VarCompositeGlyph
   get_path_at (const hb_varc_context_t &c,
 	       hb_codepoint_t gid,
 	       hb_array_t<const int> coords,
-	       hb_transform_t transform,
+	       hb_transform_t<> transform,
 	       hb_ubytes_t record,
-	       VarRegionList::cache_t *cache)
+	       hb_scalar_cache_t *cache)
   {
     while (record)
     {
@@ -104,9 +104,9 @@ struct VARC
   get_path_at (const hb_varc_context_t &c,
 	       hb_codepoint_t gid,
 	       hb_array_t<const int> coords,
-	       hb_transform_t transform = HB_TRANSFORM_IDENTITY,
+	       hb_transform_t<> transform = HB_TRANSFORM_IDENTITY,
 	       hb_codepoint_t parent_gid = HB_CODEPOINT_INVALID,
-	       VarRegionList::cache_t *parent_cache = nullptr) const;
+	       hb_scalar_cache_t *parent_cache = nullptr) const;
 
   bool
   get_path (hb_font_t *font,
@@ -129,7 +129,7 @@ struct VARC
   bool
   get_extents (hb_font_t *font,
 	       hb_codepoint_t gid,
-	       hb_extents_t *extents,
+	       hb_extents_t<> *extents,
 	       hb_varc_scratch_t &scratch) const
   {
     hb_varc_context_t c {font,
@@ -196,7 +196,7 @@ struct VARC
     {
       if (!table->has_data ()) return false;
 
-      hb_extents_t f_extents;
+      hb_extents_t<> f_extents;
 
       auto *scratch = acquire_scratch ();
       if (unlikely (!scratch)) return true;

+ 19 - 19
thirdparty/harfbuzz/src/OT/glyf/Glyph.hh

@@ -102,17 +102,15 @@ struct Glyph
     if (unlikely (!points.resize (points.length + PHANTOM_COUNT))) return false;
     hb_array_t<contour_point_t> phantoms = points.as_array ().sub_array (points.length - PHANTOM_COUNT, PHANTOM_COUNT);
     {
+      // Duplicated code.
       int lsb = 0;
-      int h_delta = face->table.hmtx->get_leading_bearing_without_var_unscaled (gid, &lsb) ?
-                    (int) header->xMin - lsb : 0;
+      face->table.hmtx->get_leading_bearing_without_var_unscaled (gid, &lsb);
+      int h_delta = (int) header->xMin - lsb;
       HB_UNUSED int tsb = 0;
-      int v_orig  = (int) header->yMax +
 #ifndef HB_NO_VERTICAL
-                    ((void) face->table.vmtx->get_leading_bearing_without_var_unscaled (gid, &tsb), tsb)
-#else
-                    0
+      face->table.vmtx->get_leading_bearing_without_var_unscaled (gid, &tsb);
 #endif
-                    ;
+      int v_orig  = (int) header->yMax + tsb;
       unsigned h_adv = face->table.hmtx->get_advance_without_var_unscaled (gid);
       unsigned v_adv =
 #ifndef HB_NO_VERTICAL
@@ -314,6 +312,7 @@ struct Glyph
 		   bool use_my_metrics = true,
 		   bool phantom_only = false,
 		   hb_array_t<const int> coords = hb_array_t<const int> (),
+		   hb_scalar_cache_t *gvar_cache = nullptr,
 		   unsigned int depth = 0,
 		   unsigned *edge_count = nullptr) const
   {
@@ -328,7 +327,7 @@ struct Glyph
       head_maxp_info->maxComponentDepth = hb_max (head_maxp_info->maxComponentDepth, depth);
     }
 
-    if (!coords)
+    if (!coords && font->has_nonzero_coords)
       coords = hb_array (font->coords, font->num_coords);
 
     contour_point_vector_t &points = type == SIMPLE ? all_points : scratch.comp_points;
@@ -357,25 +356,23 @@ struct Glyph
     if (unlikely (!points.resize (points.length + PHANTOM_COUNT))) return false;
     hb_array_t<contour_point_t> phantoms = points.as_array ().sub_array (points.length - PHANTOM_COUNT, PHANTOM_COUNT);
     {
+      // Duplicated code.
       int lsb = 0;
-      int h_delta = glyf_accelerator.hmtx->get_leading_bearing_without_var_unscaled (gid, &lsb) ?
-		    (int) header->xMin - lsb : 0;
+      glyf_accelerator.hmtx->get_leading_bearing_without_var_unscaled (gid, &lsb);
+      int h_delta = (int) header->xMin - lsb;
       HB_UNUSED int tsb = 0;
-      int v_orig  = (int) header->yMax +
 #ifndef HB_NO_VERTICAL
-		    ((void) glyf_accelerator.vmtx->get_leading_bearing_without_var_unscaled (gid, &tsb), tsb)
-#else
-		    0
+      glyf_accelerator.vmtx->get_leading_bearing_without_var_unscaled (gid, &tsb);
 #endif
-		    ;
+      int v_orig  = (int) header->yMax + tsb;
       unsigned h_adv = glyf_accelerator.hmtx->get_advance_without_var_unscaled (gid);
       unsigned v_adv =
 #ifndef HB_NO_VERTICAL
-		       glyf_accelerator.vmtx->get_advance_without_var_unscaled (gid)
+                       glyf_accelerator.vmtx->get_advance_without_var_unscaled (gid)
 #else
-		       - font->face->get_upem ()
+                       - font->face->get_upem ()
 #endif
-		       ;
+                       ;
       phantoms[PHANTOM_LEFT].x = h_delta;
       phantoms[PHANTOM_RIGHT].x = (int) h_adv + h_delta;
       phantoms[PHANTOM_TOP].y = v_orig;
@@ -383,7 +380,7 @@ struct Glyph
     }
 
 #ifndef HB_NO_VAR
-    if (coords)
+    if (hb_any (coords))
     {
 #ifndef HB_NO_BEYOND_64K
       if (glyf_accelerator.GVAR->has_data ())
@@ -391,6 +388,7 @@ struct Glyph
 						       coords,
 						       points.as_array ().sub_array (old_length),
 						       scratch,
+						       gvar_cache,
 						       phantom_only && type == SIMPLE);
       else
 #endif
@@ -398,6 +396,7 @@ struct Glyph
 						       coords,
 						       points.as_array ().sub_array (old_length),
 						       scratch,
+						       gvar_cache,
 						       phantom_only && type == SIMPLE);
     }
 #endif
@@ -447,6 +446,7 @@ struct Glyph
 						    use_my_metrics,
 						    phantom_only,
 						    coords,
+						    gvar_cache,
 						    depth + 1,
 						    edge_count)))
 	{

+ 80 - 66
thirdparty/harfbuzz/src/OT/glyf/glyf.hh

@@ -220,7 +220,8 @@ struct glyf_accelerator_t
   template<typename T>
   bool get_points (hb_font_t *font, hb_codepoint_t gid, T consumer,
 		   hb_array_t<const int> coords,
-		   hb_glyf_scratch_t &scratch) const
+		   hb_glyf_scratch_t &scratch,
+		   hb_scalar_cache_t *gvar_cache = nullptr) const
   {
     if (gid >= num_glyphs) return false;
 
@@ -228,7 +229,7 @@ struct glyf_accelerator_t
     all_points.resize (0);
 
     bool phantom_only = !consumer.is_consuming_contour_points ();
-    if (unlikely (!glyph_for_gid (gid).get_points (font, *this, all_points, scratch, 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, gvar_cache)))
       return false;
 
     unsigned count = all_points.length;
@@ -371,69 +372,67 @@ struct glyf_accelerator_t
     contour_point_t *get_phantoms_sink () { return phantoms; }
   };
 
+#ifndef HB_NO_VAR
   unsigned
-  get_advance_with_var_unscaled (hb_font_t *font, hb_codepoint_t gid, bool is_vertical) const
+  get_advance_with_var_unscaled (hb_codepoint_t gid,
+				 hb_font_t *font,
+				 bool is_vertical,
+				  hb_glyf_scratch_t &scratch,
+				 hb_scalar_cache_t *gvar_cache = nullptr) const
   {
     if (unlikely (gid >= num_glyphs)) return 0;
 
     bool success = false;
 
     contour_point_t phantoms[glyf_impl::PHANTOM_COUNT];
-    if (font->num_coords)
+    success = get_points (font, gid, points_aggregator_t (font, nullptr, phantoms, false),
+			  hb_array (font->coords,
+				    font->has_nonzero_coords ? font->num_coords : 0),
+			  scratch, gvar_cache);
+    if (unlikely (!success))
     {
-      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);
+      unsigned upem = font->face->get_upem ();
+      return is_vertical ? upem : upem / 2;
     }
 
-    if (unlikely (!success))
-      return
-#ifndef HB_NO_VERTICAL
-	is_vertical ? vmtx->get_advance_without_var_unscaled (gid) :
-#endif
-	hmtx->get_advance_without_var_unscaled (gid);
-
     float result = is_vertical
 		 ? phantoms[glyf_impl::PHANTOM_TOP].y - phantoms[glyf_impl::PHANTOM_BOTTOM].y
 		 : phantoms[glyf_impl::PHANTOM_RIGHT].x - phantoms[glyf_impl::PHANTOM_LEFT].x;
     return hb_clamp (roundf (result), 0.f, (float) UINT_MAX / 2);
   }
 
-  bool get_leading_bearing_with_var_unscaled (hb_font_t *font, hb_codepoint_t gid, bool is_vertical, int *lsb) const
+  float
+  get_v_origin_with_var_unscaled (hb_codepoint_t gid,
+				  hb_font_t *font,
+				  hb_glyf_scratch_t &scratch,
+				  hb_scalar_cache_t *gvar_cache = nullptr) const
   {
-    if (unlikely (gid >= num_glyphs)) return false;
+    if (unlikely (gid >= num_glyphs)) return 0;
+
+    bool success = false;
 
-    hb_glyph_extents_t extents;
-    hb_glyf_scratch_t scratch;
     contour_point_t phantoms[glyf_impl::PHANTOM_COUNT];
-    if (unlikely (!get_points (font, gid, points_aggregator_t (font, &extents, phantoms, false),
-			       hb_array (font->coords, font->num_coords),
-			       scratch)))
-      return false;
+    success = get_points (font, gid, points_aggregator_t (font, nullptr, phantoms, false),
+			  hb_array (font->coords,
+				    font->has_nonzero_coords ? font->num_coords : 0),
+			  scratch, gvar_cache);
+    if (unlikely (!success))
+    {
+      return font->face->get_upem ();
+    }
 
-    *lsb = is_vertical
-	 ? roundf (phantoms[glyf_impl::PHANTOM_TOP].y) - extents.y_bearing
-	 : roundf (phantoms[glyf_impl::PHANTOM_LEFT].x);
-    return true;
+    return phantoms[glyf_impl::PHANTOM_TOP].y;
   }
 #endif
-
-  bool get_leading_bearing_without_var_unscaled (hb_codepoint_t gid, bool is_vertical, int *lsb) const
-  {
-    if (unlikely (gid >= num_glyphs)) return false;
-    if (is_vertical) return false; // TODO Humm, what to do here?
-
-    *lsb = glyph_for_gid (gid).get_header ()->xMin;
-    return true;
-  }
+#endif
 
   public:
 
   bool get_extents (hb_font_t *font,
 		    hb_codepoint_t gid,
 		    hb_glyph_extents_t *extents) const
-  { return get_extents_at (font, gid, extents, hb_array (font->coords, font->num_coords)); }
+  { return get_extents_at (font, gid, extents, hb_array (font->coords,
+							 font->has_nonzero_coords ? font->num_coords : 0)); }
 
   bool get_extents_at (hb_font_t *font,
 		       hb_codepoint_t gid,
@@ -445,12 +444,16 @@ struct glyf_accelerator_t
 #ifndef HB_NO_VAR
     if (coords)
     {
-      hb_glyf_scratch_t scratch;
-      return get_points (font,
-			 gid,
-			 points_aggregator_t (font, extents, nullptr, true),
-			 coords,
-			 scratch);
+      hb_glyf_scratch_t *scratch = acquire_scratch ();
+      if (unlikely (!scratch))
+        return false;
+      bool ret = get_points (font,
+			     gid,
+			     points_aggregator_t (font, extents, nullptr, true),
+			     coords,
+			     *scratch);
+      release_scratch (scratch);
+      return ret;
     }
 #endif
     return glyph_for_gid (gid).get_extents_without_var_scaled (font, *this, extents);
@@ -485,33 +488,21 @@ struct glyf_accelerator_t
   }
 
   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, hb_scalar_cache_t *gvar_cache = nullptr) const
   {
     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;
-      }
-    }
+    hb_glyf_scratch_t *scratch = acquire_scratch ();
+    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);
+			   hb_array (font->coords,
+				     font->has_nonzero_coords ? font->num_coords : 0),
+			   *scratch,
+			    gvar_cache);
 
-    // Put it back.
-    if (!cached_scratch.cmpexch (nullptr, scratch))
-    {
-      scratch->~hb_glyf_scratch_t ();
-      hb_free (scratch);
-    }
+    release_scratch (scratch);
 
     return ret;
   }
@@ -519,12 +510,35 @@ struct glyf_accelerator_t
   bool
   get_path_at (hb_font_t *font, hb_codepoint_t gid, hb_draw_session_t &draw_session,
 	       hb_array_t<const int> coords,
-	       hb_glyf_scratch_t &scratch) const
+	       hb_glyf_scratch_t &scratch,
+	       hb_scalar_cache_t *gvar_cache = nullptr) const
   {
     if (!has_data ()) return false;
     return get_points (font, gid, glyf_impl::path_builder_t (font, draw_session),
 		       coords,
-		       scratch);
+		       scratch,
+		       gvar_cache);
+  }
+
+
+  hb_glyf_scratch_t *acquire_scratch () const
+  {
+    hb_glyf_scratch_t *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 nullptr;
+    }
+    return scratch;
+  }
+  void release_scratch (hb_glyf_scratch_t *scratch) const
+  {
+    if (!cached_scratch.cmpexch (nullptr, scratch))
+    {
+      scratch->~hb_glyf_scratch_t ();
+      hb_free (scratch);
+    }
   }
 
 #ifndef HB_NO_VAR

+ 1 - 1
thirdparty/harfbuzz/src/graph/classdef-graph.hh

@@ -74,7 +74,7 @@ struct ClassDef : public OT::ClassDef
     class_def_link->width = SmallTypes::size;
     class_def_link->objidx = class_def_prime_id;
     class_def_link->position = link_position;
-    class_def_prime_vertex.add_parent (parent_id);
+    class_def_prime_vertex.add_parent (parent_id, false);
 
     return true;
   }

+ 23 - 1
thirdparty/harfbuzz/src/graph/coverage-graph.hh

@@ -98,11 +98,33 @@ struct Coverage : public OT::Layout::Common::Coverage
     coverage_link->width = SmallTypes::size;
     coverage_link->objidx = coverage_prime_id;
     coverage_link->position = link_position;
-    coverage_prime_vertex.add_parent (parent_id);
+    coverage_prime_vertex.add_parent (parent_id, false);
 
     return (Coverage*) coverage_prime_vertex.obj.head;
   }
 
+  // Filter an existing coverage table to glyphs at indices [start, end) and replace it with the filtered version.
+  static bool filter_coverage (gsubgpos_graph_context_t& c,
+                               unsigned existing_coverage,
+                               unsigned start, unsigned end) {
+    unsigned coverage_size = c.graph.vertices_[existing_coverage].table_size ();
+    auto& coverage_v = c.graph.vertices_[existing_coverage];
+    Coverage* coverage_table = (Coverage*) coverage_v.obj.head;
+    if (!coverage_table || !coverage_table->sanitize (coverage_v))
+      return false;
+
+    auto new_coverage =
+        + hb_zip (coverage_table->iter (), hb_range ())
+        | hb_filter ([&] (hb_pair_t<unsigned, unsigned> p) {
+          return p.second >= start && p.second < end;
+        })
+        | hb_map_retains_sorting (hb_first)
+        ;
+
+    return make_coverage (c, new_coverage, existing_coverage, coverage_size * 2 + 100);
+  }
+
+  // Replace the coverage table at dest obj with one covering 'glyphs'.
   template<typename It>
   static bool make_coverage (gsubgpos_graph_context_t& c,
                              It glyphs,

+ 64 - 19
thirdparty/harfbuzz/src/graph/graph.hh

@@ -50,6 +50,7 @@ struct graph_t
     private:
     unsigned incoming_edges_ = 0;
     unsigned single_parent = (unsigned) -1;
+    bool has_incoming_virtual_edges_ = false;
     hb_hashmap_t<unsigned, unsigned> parents;
     public:
 
@@ -66,6 +67,11 @@ struct graph_t
       return parents.in_error ();
     }
 
+    bool has_incoming_virtual_edges () const
+    {
+      return has_incoming_virtual_edges_;
+    }
+
     bool link_positions_valid (unsigned num_objects, bool removed_nil)
     {
       hb_set_t assigned_bytes;
@@ -121,7 +127,9 @@ struct graph_t
       }
     }
 
-    bool equals (const vertex_t& other,
+    bool equals (unsigned this_index,
+                 unsigned other_index,
+                 const vertex_t& other,
                  const graph_t& graph,
                  const graph_t& other_graph,
                  unsigned depth) const
@@ -129,8 +137,10 @@ struct graph_t
       if (!(as_bytes () == other.as_bytes ()))
       {
         DEBUG_MSG (SUBSET_REPACK, nullptr,
-                   "vertex [%lu] bytes != [%lu] bytes, depth = %u",
+                   "vertex %u [%lu bytes] != %u [%lu bytes], depth = %u",
+                   this_index,
                    (unsigned long) table_size (),
+                   other_index,
                    (unsigned long) other.table_size (),
                    depth);
 
@@ -162,6 +172,7 @@ struct graph_t
       hb_swap (a.single_parent, b.single_parent);
       hb_swap (a.parents, b.parents);
       hb_swap (a.incoming_edges_, b.incoming_edges_);
+      hb_swap (a.has_incoming_virtual_edges_, b.has_incoming_virtual_edges_);
       hb_swap (a.start, b.start);
       hb_swap (a.end, b.end);
       hb_swap (a.priority, b.priority);
@@ -207,13 +218,16 @@ struct graph_t
     void reset_parents ()
     {
       incoming_edges_ = 0;
+      has_incoming_virtual_edges_ = false;
       single_parent = (unsigned) -1;
       parents.reset ();
     }
 
-    void add_parent (unsigned parent_index)
+    void add_parent (unsigned parent_index, bool is_virtual)
     {
       assert (parent_index != (unsigned) -1);
+      has_incoming_virtual_edges_ |= is_virtual;
+
       if (incoming_edges_ == 0)
       {
 	single_parent = parent_index;
@@ -408,7 +422,7 @@ struct graph_t
             link_a.bias != link_b.bias)
           return false;
 
-        if (!graph.vertices_[link_a.objidx].equals (
+        if (!graph.vertices_[link_a.objidx].equals (link_a.objidx, link_b.objidx,
                 other_graph.vertices_[link_b.objidx], graph, other_graph, depth + 1))
           return false;
 
@@ -490,7 +504,7 @@ struct graph_t
 
   bool operator== (const graph_t& other) const
   {
-    return root ().equals (other.root (), *this, other, 0);
+    return root ().equals (root_idx(), other.root_idx(), other.root (), *this, other, 0);
   }
 
   void print () const {
@@ -501,6 +515,9 @@ struct graph_t
       for (const auto &l : v.obj.real_links) {
         printf("%u, ", l.objidx);
       }
+      for (const auto &l : v.obj.virtual_links) {
+        printf("v%u, ", l.objidx);
+      }
       printf("]\n");
     }
   }
@@ -556,7 +573,7 @@ struct graph_t
     link->width = 2;
     link->objidx = child_id;
     link->position = (char*) offset - (char*) v.obj.head;
-    vertices_[child_id].add_parent (parent_id);
+    vertices_[child_id].add_parent (parent_id, false);
   }
 
   /*
@@ -943,9 +960,11 @@ struct graph_t
   /*
    * Moves the child of old_parent_idx pointed to by old_offset to a new
    * vertex at the new_offset.
+   *
+   * Returns the id of the child node that was moved.
    */
   template<typename O>
-  void move_child (unsigned old_parent_idx,
+  unsigned move_child (unsigned old_parent_idx,
                    const O* old_offset,
                    unsigned new_parent_idx,
                    const O* new_offset)
@@ -965,10 +984,12 @@ struct graph_t
     new_link->position = (const char*) new_offset - (const char*) new_v.obj.head;
 
     auto& child = vertices_[child_id];
-    child.add_parent (new_parent_idx);
+    child.add_parent (new_parent_idx, false);
 
     old_v.remove_real_link (child_id, old_offset);
     child.remove_parent (old_parent_idx);
+
+    return child_id;
   }
 
   /*
@@ -1015,12 +1036,12 @@ struct graph_t
     for (const auto& l : child.obj.real_links)
     {
       clone->obj.real_links.push (l);
-      vertices_[l.objidx].add_parent (clone_idx);
+      vertices_[l.objidx].add_parent (clone_idx, false);
     }
     for (const auto& l : child.obj.virtual_links)
     {
       clone->obj.virtual_links.push (l);
-      vertices_[l.objidx].add_parent (clone_idx);
+      vertices_[l.objidx].add_parent (clone_idx, true);
     }
 
     check_success (!clone->obj.real_links.in_error ());
@@ -1073,10 +1094,15 @@ struct graph_t
     const auto& child = vertices_[child_idx];
     unsigned links_to_child = child.incoming_edges_from_parent(parent_idx);
 
-    if (child.incoming_edges () <= links_to_child)
+    if (child.incoming_edges () <= links_to_child || child.has_incoming_virtual_edges())
     {
       // Can't duplicate this node, doing so would orphan the original one as all remaining links
       // to child are from parent.
+      //
+      // We don't allow duplication of nodes with incoming virtual edges because we don't track
+      // the number of virtual vs real incoming edges. As a result we can't tell if a node
+      // with virtual edges may end up orphaned by duplication (ie. where one copy is only pointed
+      // to by virtual edges).
       DEBUG_MSG (SUBSET_REPACK, nullptr, "  Not duplicating %u => %u",
                  parent_idx, child_idx);
       return -1;
@@ -1091,12 +1117,15 @@ struct graph_t
     if (parent_idx == clone_idx) parent_idx++;
 
     auto& parent = vertices_[parent_idx];
+    unsigned count = 0;
+    unsigned num_real = parent.obj.real_links.length;
     for (auto& l : parent.obj.all_links_writer ())
     {
+      count++;
       if (l.objidx != child_idx)
         continue;
 
-      reassign_link (l, parent_idx, clone_idx);
+      reassign_link (l, parent_idx, clone_idx, count > num_real);
     }
 
     return clone_idx;
@@ -1129,10 +1158,15 @@ struct graph_t
       links_to_child += child.incoming_edges_from_parent(parent_idx);
     }
 
-    if (child.incoming_edges () <= links_to_child)
+    if (child.incoming_edges () <= links_to_child || child.has_incoming_virtual_edges())
     {
       // Can't duplicate this node, doing so would orphan the original one as all remaining links
       // to child are from parent.
+      //
+      // We don't allow duplication of nodes with incoming virtual edges because we don't track
+      // the number of virtual vs real incoming edges. As a result we can't tell if a node
+      // with virtual edges may end up orphaned by duplication (ie. where one copy is only pointed
+      // to by virtual edges).
       DEBUG_MSG (SUBSET_REPACK, nullptr, "  Not duplicating %u, ..., %u => %u", first_parent, last_parent, child_idx);
       return -1;
     }
@@ -1146,12 +1180,15 @@ struct graph_t
       // duplicate shifts the root node idx, so if parent_idx was root update it.
       if (parent_idx == clone_idx) parent_idx++;
       auto& parent = vertices_[parent_idx];
+      unsigned count = 0;
+      unsigned num_real = parent.obj.real_links.length;
       for (auto& l : parent.obj.all_links_writer ())
       {
+        count++;
         if (l.objidx != child_idx)
           continue;
 
-        reassign_link (l, parent_idx, clone_idx);
+        reassign_link (l, parent_idx, clone_idx, count > num_real);
       }
     }
 
@@ -1279,6 +1316,7 @@ struct graph_t
     if (!DEBUG_ENABLED(SUBSET_REPACK)) return;
 
     DEBUG_MSG (SUBSET_REPACK, nullptr, "Graph is not fully connected.");
+
     parents_invalid = true;
     update_parents();
 
@@ -1398,8 +1436,11 @@ struct graph_t
 
     for (unsigned p = 0; p < count; p++)
     {
-      for (auto& l : vertices_.arrayZ[p].obj.all_links ())
-        vertices_[l.objidx].add_parent (p);
+      for (auto& l : vertices_.arrayZ[p].obj.real_links)
+        vertices_[l.objidx].add_parent (p, false);
+
+      for (auto& l : vertices_.arrayZ[p].obj.virtual_links)
+        vertices_[l.objidx].add_parent (p, true);
     }
 
     for (unsigned i = 0; i < count; i++)
@@ -1502,12 +1543,13 @@ struct graph_t
    */
   void reassign_link (hb_serialize_context_t::object_t::link_t& link,
                       unsigned parent_idx,
-                      unsigned new_idx)
+                      unsigned new_idx,
+                      bool is_virtual)
   {
     unsigned old_idx = link.objidx;
     link.objidx = new_idx;
     vertices_[old_idx].remove_parent (parent_idx);
-    vertices_[new_idx].add_parent (parent_idx);
+    vertices_[new_idx].add_parent (parent_idx, is_virtual);
   }
 
   /*
@@ -1521,13 +1563,16 @@ struct graph_t
     if (!id_map) return;
     for (unsigned i : subgraph)
     {
+      unsigned num_real = vertices_[i].obj.real_links.length;
+      unsigned count = 0;
       for (auto& link : vertices_[i].obj.all_links_writer ())
       {
+        count++;
         const uint32_t *v;
         if (!id_map.has (link.objidx, &v)) continue;
         if (only_wide && !(link.width == 4 && !link.is_signed)) continue;
 
-        reassign_link (link, i, *v);
+        reassign_link (link, i, *v, count > num_real);
       }
     }
   }

+ 50 - 24
thirdparty/harfbuzz/src/graph/gsubgpos-graph.hh

@@ -27,9 +27,11 @@
 #include "graph.hh"
 #include "../hb-ot-layout-gsubgpos.hh"
 #include "../OT/Layout/GSUB/ExtensionSubst.hh"
+#include "../OT/Layout/GSUB/SubstLookupSubTable.hh"
 #include "gsubgpos-context.hh"
 #include "pairpos-graph.hh"
 #include "markbasepos-graph.hh"
+#include "ligature-graph.hh"
 
 #ifndef GRAPH_GSUBGPOS_GRAPH_HH
 #define GRAPH_GSUBGPOS_GRAPH_HH
@@ -120,12 +122,10 @@ struct Lookup : public OT::Lookup
     unsigned type = lookupType;
     bool is_ext = is_extension (c.table_tag);
 
-    if (c.table_tag != HB_OT_TAG_GPOS)
+    if (c.table_tag != HB_OT_TAG_GPOS && c.table_tag != HB_OT_TAG_GSUB)
       return true;
 
-    if (!is_ext &&
-        type != OT::Layout::GPOS_impl::PosLookupSubTable::Type::Pair &&
-        type != OT::Layout::GPOS_impl::PosLookupSubTable::Type::MarkBase)
+    if (!is_ext && !is_supported_gpos_type(type, c) && !is_supported_gsub_type(type, c))
       return true;
 
     hb_vector_t<hb_pair_t<unsigned, hb_vector_t<unsigned>>> all_new_subtables;
@@ -144,21 +144,32 @@ struct Lookup : public OT::Lookup
 
         subtable_index = extension->get_subtable_index (c.graph, ext_subtable_index);
         type = extension->get_lookup_type ();
-        if (type != OT::Layout::GPOS_impl::PosLookupSubTable::Type::Pair
-            && type != OT::Layout::GPOS_impl::PosLookupSubTable::Type::MarkBase)
+        if (!is_supported_gpos_type(type, c) && !is_supported_gsub_type(type, c))
           continue;
       }
 
       hb_vector_t<unsigned> new_sub_tables;
-      switch (type)
-      {
-      case 2:
-        new_sub_tables = split_subtable<PairPos> (c, parent_index, subtable_index); break;
-      case 4:
-        new_sub_tables = split_subtable<MarkBasePos> (c, parent_index, subtable_index); break;
-      default:
-        break;
+
+      if (c.table_tag == HB_OT_TAG_GPOS) {
+        switch (type)
+        {
+        case 2:
+          new_sub_tables = split_subtable<PairPos> (c, parent_index, subtable_index); break;
+        case 4:
+          new_sub_tables = split_subtable<MarkBasePos> (c, parent_index, subtable_index); break;
+        default:
+          break;
+        }
+      } else if (c.table_tag == HB_OT_TAG_GSUB) {
+        switch (type)
+        {
+        case 4:
+          new_sub_tables = split_subtable<graph::LigatureSubst> (c, parent_index, subtable_index); break;
+        default:
+          break;
+        }
       }
+
       if (new_sub_tables.in_error ()) return false;
       if (!new_sub_tables) continue;
       hb_pair_t<unsigned, hb_vector_t<unsigned>>* entry = all_new_subtables.push ();
@@ -191,14 +202,14 @@ struct Lookup : public OT::Lookup
                        hb_vector_t<hb_pair_t<unsigned, hb_vector_t<unsigned>>>& subtable_ids)
   {
     bool is_ext = is_extension (c.table_tag);
-    auto& v = c.graph.vertices_[this_index];
+    auto* v = &c.graph.vertices_[this_index];
     fix_existing_subtable_links (c, this_index, subtable_ids);
 
     unsigned new_subtable_count = 0;
     for (const auto& p : subtable_ids)
       new_subtable_count += p.second.length;
 
-    size_t new_size = v.table_size ()
+    size_t new_size = v->table_size ()
                       + new_subtable_count * OT::Offset16::static_size;
     char* buffer = (char*) hb_calloc (1, new_size);
     if (!buffer) return false;
@@ -207,10 +218,10 @@ struct Lookup : public OT::Lookup
       hb_free (buffer);
      return false;
     }
-    hb_memcpy (buffer, v.obj.head, v.table_size());
+    hb_memcpy (buffer, v->obj.head, v->table_size());
 
-    v.obj.head = buffer;
-    v.obj.tail = buffer + new_size;
+    v->obj.head = buffer;
+    v->obj.tail = buffer + new_size;
 
     Lookup* new_lookup = (Lookup*) buffer;
 
@@ -226,21 +237,23 @@ struct Lookup : public OT::Lookup
         if (is_ext)
         {
           unsigned ext_id = create_extension_subtable (c, subtable_id, type);
-          c.graph.vertices_[subtable_id].add_parent (ext_id);
+          c.graph.vertices_[subtable_id].add_parent (ext_id, false);
           subtable_id = ext_id;
+          // the reference to v may have changed on adding a node, so reassign it.
+          v = &c.graph.vertices_[this_index];
         }
 
-        auto* link = v.obj.real_links.push ();
+        auto* link = v->obj.real_links.push ();
         link->width = 2;
         link->objidx = subtable_id;
         link->position = (char*) &new_lookup->subTable[offset_index++] -
                          (char*) new_lookup;
-        c.graph.vertices_[subtable_id].add_parent (this_index);
+        c.graph.vertices_[subtable_id].add_parent (this_index, false);
       }
     }
 
     // Repacker sort order depends on link order, which we've messed up so resort it.
-    v.obj.real_links.qsort ();
+    v->obj.real_links.qsort ();
 
     // The head location of the lookup has changed, invalidating the lookups map entry
     // in the context. Update the map.
@@ -326,7 +339,7 @@ struct Lookup : public OT::Lookup
 
     // Make extension point at the subtable.
     auto& ext_vertex = c.graph.vertices_[ext_index];
-    ext_vertex.add_parent (lookup_index);
+    ext_vertex.add_parent (lookup_index, false);
     if (!existing_ext_index)
       subtable_vertex.remap_parent (lookup_index, ext_index);
 
@@ -334,6 +347,19 @@ struct Lookup : public OT::Lookup
   }
 
  private:
+  bool is_supported_gsub_type(unsigned type, gsubgpos_graph_context_t& c) const {
+    return (c.table_tag == HB_OT_TAG_GSUB) && (
+      type == OT::Layout::GSUB_impl::SubstLookupSubTable::Type::Ligature
+    );
+  }
+
+  bool is_supported_gpos_type(unsigned type, gsubgpos_graph_context_t& c) const {
+   return (c.table_tag == HB_OT_TAG_GPOS) && (
+      type == OT::Layout::GPOS_impl::PosLookupSubTable::Type::Pair ||
+      type == OT::Layout::GPOS_impl::PosLookupSubTable::Type::MarkBase
+    );
+  }
+
   unsigned extension_type (hb_tag_t table_tag) const
   {
     switch (table_tag)

+ 480 - 0
thirdparty/harfbuzz/src/graph/ligature-graph.hh

@@ -0,0 +1,480 @@
+/*
+ * Copyright © 2025  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Garret Rieger
+ */
+
+#ifndef GRAPH_LIGATURE_GRAPH_HH
+#define GRAPH_LIGATURE_GRAPH_HH
+
+#include "graph.hh"
+#include "../OT/Layout/GSUB/LigatureSubst.hh"
+#include "../OT/Layout/GSUB/LigatureSubstFormat1.hh"
+#include "../OT/Layout/GSUB/LigatureSet.hh"
+#include "../OT/Layout/types.hh"
+#include <algorithm>
+#include <utility>
+
+namespace graph {
+
+struct LigatureSet : public OT::Layout::GSUB_impl::LigatureSet<SmallTypes>
+{
+  bool sanitize (graph_t::vertex_t& vertex) const
+  {
+    int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
+    if (vertex_len < OT::Layout::GSUB_impl::LigatureSet<SmallTypes>::min_size) return false;
+    hb_barrier ();
+
+    int64_t total_len = ligature.get_size() + OT::Layout::GSUB_impl::LigatureSet<SmallTypes>::min_size - ligature.len.get_size();
+    if (vertex_len < total_len) {
+      return false;
+    }
+    return true;
+  }
+};
+
+struct LigatureSubstFormat1 : public OT::Layout::GSUB_impl::LigatureSubstFormat1_2<SmallTypes>
+{
+  bool sanitize (graph_t::vertex_t& vertex) const
+  {
+    int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
+    unsigned min_size = OT::Layout::GSUB_impl::LigatureSubstFormat1_2<SmallTypes>::min_size;
+    if (vertex_len < min_size) return false;
+    hb_barrier ();
+
+    return vertex_len >=
+        min_size + ligatureSet.get_size() - ligatureSet.len.get_size();
+  }
+
+  hb_vector_t<unsigned> split_subtables (gsubgpos_graph_context_t& c,
+                                         unsigned parent_index,
+                                         unsigned this_index)
+  {
+    auto split_points = compute_split_points(c, parent_index, this_index);
+    split_context_t split_context {
+      c,
+      this,
+      c.graph.duplicate_if_shared (parent_index, this_index),
+      total_number_ligas(c, this_index),
+      liga_counts(c, this_index),
+    };
+    return actuate_subtable_split<split_context_t> (split_context, split_points);
+  }
+
+ private:
+  unsigned total_number_ligas(gsubgpos_graph_context_t& c, unsigned this_index) const {
+    unsigned total = 0;
+    for (unsigned i = 0; i < ligatureSet.len; i++)
+    {
+      auto liga_set = c.graph.as_table<LigatureSet>(this_index, &ligatureSet[i]);
+      if (!liga_set.table) {
+        return 0;
+      }
+      total += liga_set.table->ligature.len;
+    }
+    return total;
+  }
+
+  hb_vector_t<unsigned> liga_counts(gsubgpos_graph_context_t& c, unsigned this_index) const {
+    hb_vector_t<unsigned> result;
+    for (unsigned i = 0; i < ligatureSet.len; i++)
+    {
+      auto liga_set = c.graph.as_table<LigatureSet>(this_index, &ligatureSet[i]);
+      result.push(!liga_set.table ? 0 : liga_set.table->ligature.len);
+    }
+    return result;
+  }
+
+  hb_vector_t<unsigned> compute_split_points(gsubgpos_graph_context_t& c,
+                                             unsigned parent_index,
+                                             unsigned this_index) const
+  {
+    // For ligature subst coverage is always packed last, and as a result is where an overflow
+    // will happen if there is one, so we can check the estimate length of the
+    // LigatureSubstFormat1 -> Coverage offset length which is the sum of all data in the
+    // retained sub graph except for the coverage table itself.
+    const unsigned base_size = OT::Layout::GSUB_impl::LigatureSubstFormat1_2<SmallTypes>::min_size;
+    unsigned accumulated = base_size;
+
+    unsigned ligature_index = 0;
+    hb_vector_t<unsigned> split_points;
+    for (unsigned i = 0; i < ligatureSet.len; i++)
+    {
+      accumulated += OT::HBUINT16::static_size; // for ligature set offset
+      accumulated += OT::Layout::GSUB_impl::LigatureSet<SmallTypes>::min_size; // for ligature set table
+
+      auto liga_set = c.graph.as_table<LigatureSet>(this_index, &ligatureSet[i]);
+      if (!liga_set.table) {
+        return hb_vector_t<unsigned> {};
+      }
+
+      for (unsigned j = 0; j < liga_set.table->ligature.len; j++)
+      {
+        const unsigned liga_id = c.graph.index_for_offset (liga_set.index, &liga_set.table->ligature[j]);
+        const unsigned liga_size = c.graph.vertices_[liga_id].table_size ();
+
+        accumulated += OT::HBUINT16::static_size; // for ligature offset
+        accumulated += liga_size; // for the ligature table
+
+        if (accumulated >= (1 << 16))
+        {
+          split_points.push(ligature_index);
+          // We're going to split such that the current ligature will be in the new sub table.
+          // That means we'll have one ligature subst (base_base), one ligature set, and one liga table
+          accumulated = base_size + // for liga subst subtable
+            (OT::HBUINT16::static_size * 2) + // for liga set and liga offset
+            OT::Layout::GSUB_impl::LigatureSet<SmallTypes>::min_size + // for liga set subtable
+            liga_size; // for liga sub table
+        }
+
+        ligature_index++;
+      }
+    }
+
+    return split_points;
+  }
+
+
+  struct split_context_t
+  {
+    gsubgpos_graph_context_t& c;
+    LigatureSubstFormat1* thiz;
+    unsigned this_index;
+    unsigned original_count_;
+    hb_vector_t<unsigned> liga_counts;
+
+    unsigned original_count ()
+    {
+      return original_count_;
+    }
+
+    unsigned clone_range (unsigned start, unsigned end)
+    {
+      return thiz->clone_range (c, this_index, liga_counts, start, end);
+    }
+
+    bool shrink (unsigned count)
+    {
+      return thiz->shrink (c, this_index, original_count(), liga_counts, count);
+    }
+  };
+
+  hb_pair_t<unsigned, LigatureSet*> new_liga_set(gsubgpos_graph_context_t& c, unsigned count) const {
+    unsigned prime_size = OT::Layout::GSUB_impl::LigatureSet<SmallTypes>::min_size
+                          + count * SmallTypes::size;
+
+    unsigned prime_id = c.create_node (prime_size);
+    if (prime_id == (unsigned) -1) return hb_pair(-1, nullptr);
+
+    LigatureSet* prime = (LigatureSet*) c.graph.object (prime_id).head;
+    prime->ligature.len = count;
+    return hb_pair(prime_id, prime);
+  }
+
+  void clear_virtual_links (gsubgpos_graph_context_t& c, unsigned node_index) const
+  {
+    auto& obj = c.graph.vertices_[node_index].obj;
+    for (const auto& l : obj.virtual_links)
+    {
+      auto& child = c.graph.vertices_[l.objidx];
+      child.remove_parent(node_index);
+    }
+    obj.virtual_links.clear();
+  }
+
+  void add_virtual_link(gsubgpos_graph_context_t& c, unsigned from, unsigned to) const {
+    auto& from_obj = c.graph.vertices_[from].obj;
+    c.graph.vertices_[to].add_parent(from, true);
+    auto& link = *from_obj.virtual_links.push ();
+    link.objidx = to;
+  }
+
+  hb_pair_t<unsigned, unsigned> current_liga_set_bounds (gsubgpos_graph_context_t& c,
+                                                         unsigned liga_set_index,
+                                                         const hb_serialize_context_t::object_t& liga_set) const
+  {
+    // Finds the actual liga indices present in the liga set currently. Takes
+    // into account those that have been removed by processing.
+    unsigned min_index = (unsigned) -1;
+    unsigned max_index = 0;
+    for (const auto& l : liga_set.real_links) {
+      if (l.position < 2) continue;
+
+      unsigned liga_index = (l.position - 2) / 2;
+      min_index = hb_min(min_index, liga_index);
+      max_index = hb_max(max_index, liga_index);
+    }
+    return hb_pair(min_index, max_index + 1);
+  }
+
+  void compact_liga_set (gsubgpos_graph_context_t& c, LigatureSet* table, hb_serialize_context_t::object_t& obj) const
+  {
+    if (table->ligature.len <= obj.real_links.length) return;
+
+    // compact the remaining linked liga offsets into a continous array and shrink the node as needed.
+    unsigned to_remove = table->ligature.len - obj.real_links.length;
+    unsigned new_position = SmallTypes::size;
+    obj.real_links.qsort(); // for this to work we need to process links in order of position.
+    for (auto& l : obj.real_links)
+    {
+      l.position = new_position;
+      new_position += SmallTypes::size;
+    }
+
+    table->ligature.len = obj.real_links.length;
+    obj.tail -= to_remove * SmallTypes::size;
+  }
+
+  unsigned clone_range (gsubgpos_graph_context_t& c,
+                        unsigned this_index,
+                        hb_vector_t<unsigned> liga_counts,
+                        unsigned start, unsigned end) const
+  {
+    DEBUG_MSG (SUBSET_REPACK, nullptr,
+               "  Cloning LigatureSubstFormat1 (%u) range [%u, %u).", this_index, start, end);
+
+    // Create an oversized new liga subst, we'll adjust the size down later. We don't know
+    // the final size until we process it but we also need it to exist while we're processing
+    // so that nodes can be moved to it as needed.
+    unsigned prime_size = OT::Layout::GSUB_impl::LigatureSubstFormat1_2<SmallTypes>::min_size
+                          + ligatureSet.get_size() - ligatureSet.len.get_size();
+
+    unsigned liga_subst_prime_id = c.create_node (prime_size);
+    if (liga_subst_prime_id == (unsigned) -1) return -1;
+
+    LigatureSubstFormat1* liga_subst_prime = (LigatureSubstFormat1*) c.graph.object (liga_subst_prime_id).head;
+    liga_subst_prime->format = this->format;
+    liga_subst_prime->ligatureSet.len = this->ligatureSet.len;
+
+    // Create a place holder coverage prime id since we need to add virtual links to it while
+    // generating liga and liga sets. Afterwards it will be updated to have the correct coverage.
+    unsigned coverage_id = c.graph.index_for_offset (this_index, &coverage);
+    unsigned coverage_prime_id = c.graph.duplicate(coverage_id);
+    auto& coverage_prime_vertex = c.graph.vertices_[coverage_prime_id];
+    auto* coverage_prime_link = c.graph.vertices_[liga_subst_prime_id].obj.real_links.push ();
+    coverage_prime_link->width = SmallTypes::size;
+    coverage_prime_link->objidx = coverage_prime_id;
+    coverage_prime_link->position = 2;
+    coverage_prime_vertex.add_parent (liga_subst_prime_id, false);
+
+    // Locate all liga sets with ligas between start and end.
+    // Clone or move them as needed.
+    unsigned count = 0;
+    unsigned liga_set_count = 0;
+    unsigned liga_set_start = -1;
+    unsigned liga_set_end = 0; // inclusive
+    for (unsigned i = 0; i < liga_counts.length; i++)
+    {
+      unsigned num_ligas = liga_counts[i];
+
+      unsigned current_start = count;
+      unsigned current_end = count + num_ligas;
+
+      if (current_start >= end || start >= current_end) {
+        // No intersection, so just skip
+        count += num_ligas;
+        continue;
+      }
+
+      auto liga_set_index = c.graph.index_for_offset(this_index, &ligatureSet[i]);
+      auto liga_set = c.graph.as_table<LigatureSet>(this_index, &ligatureSet[i]);
+      if (!liga_set.table) {
+        return -1;
+      }
+
+      // Bounds may need to be adjusted if some ligas have been previously removed.
+      hb_pair_t<unsigned, unsigned> liga_bounds = current_liga_set_bounds(c, liga_set_index, liga_set.vertex->obj);
+      current_start = hb_max(count + liga_bounds.first, current_start);
+      current_end = hb_min(count + liga_bounds.second, current_end);
+
+      unsigned liga_set_prime_id;
+      if (current_start >= start && current_end <= end) {
+        // This liga set is fully contined within [start, end)
+        // We can move the entire ligaset to the new liga subset object.
+        liga_set_end = i;
+        if (i < liga_set_start) liga_set_start = i;
+        liga_set_prime_id = c.graph.move_child<> (this_index,
+                              &ligatureSet[i],
+                              liga_subst_prime_id,
+                              &liga_subst_prime->ligatureSet[liga_set_count++]);
+        compact_liga_set(c, liga_set.table, liga_set.vertex->obj);
+      }
+      else
+      {
+        // This liga set partially overlaps [start, end). We'll need to create
+        // a new liga set sub table and move the intersecting ligas to it.
+        unsigned liga_count = hb_min(end, current_end) - hb_max(start, current_start);
+        auto result = new_liga_set(c, liga_count);
+        liga_set_prime_id = result.first;
+        LigatureSet* prime = result.second;
+        if (liga_set_prime_id == (unsigned) -1) return -1;
+
+        unsigned new_index = 0;
+        for (unsigned j = hb_max(start, current_start) - count; j < hb_min(end, current_end) - count; j++) {
+          c.graph.move_child<> (liga_set_index,
+                                &liga_set.table->ligature[j],
+                                liga_set_prime_id,
+                                &prime->ligature[new_index++]);
+        }
+
+        liga_set_end = i;
+        if (i < liga_set_start) liga_set_start = i;
+        c.graph.add_link(&liga_subst_prime->ligatureSet[liga_set_count++], liga_subst_prime_id, liga_set_prime_id);
+      }
+
+      // The new liga and all children set needs to have a virtual link to the new coverage table:
+      auto& liga_set_prime = c.graph.vertices_[liga_set_prime_id].obj;
+      clear_virtual_links(c, liga_set_prime_id);
+      add_virtual_link(c, liga_set_prime_id, coverage_prime_id);
+      for (const auto& l : liga_set_prime.real_links) {
+        clear_virtual_links(c, l.objidx);
+        add_virtual_link(c, l.objidx, coverage_prime_id);
+      }
+
+      count += num_ligas;
+    }
+
+    c.graph.vertices_[liga_subst_prime_id].obj.tail -= (liga_subst_prime->ligatureSet.len - liga_set_count) * SmallTypes::size;
+    liga_subst_prime->ligatureSet.len = liga_set_count;
+
+    if (!Coverage::filter_coverage (c,
+                                    coverage_prime_id,
+                                    liga_set_start, liga_set_end + 1))
+      return -1;
+
+    return liga_subst_prime_id;
+  }
+
+  bool shrink (gsubgpos_graph_context_t& c,
+               unsigned this_index,
+               unsigned old_count,
+               hb_vector_t<unsigned> liga_counts,
+               unsigned count)
+  {
+    DEBUG_MSG (SUBSET_REPACK, nullptr,
+               "  Shrinking LigatureSubstFormat1 (%u) to [0, %u).",
+               this_index,
+               count);
+    if (count >= old_count)
+      return true;
+
+    hb_set_t retained_indices;
+    unsigned new_liga_set_count = 0;
+    for (unsigned i = 0; i < liga_counts.length; i++)
+    {
+      auto liga_set = c.graph.as_table<LigatureSet>(this_index, &ligatureSet[i]);
+      if (!liga_set.table) {
+        return false;
+      }
+
+      // We need the virtual links to coverage removed from all descendants on this liga subst.
+      // If any are left when we try to mutate the coverage table later it will be unnessecarily
+      // duplicated. Code later on will re-add the virtual links as needed (via retained_indices).
+      clear_virtual_links(c, liga_set.index);
+      retained_indices.add(liga_set.index);
+      for (const auto& liga_offset : liga_set.table->ligature) {
+        unsigned liga_index = c.graph.index_for_offset(liga_set.index, &liga_offset);
+        if (liga_index != (unsigned) -1) {
+          clear_virtual_links(c, liga_index);
+          retained_indices.add(liga_index);
+        }
+      }
+
+      unsigned num_ligas = liga_counts[i];
+      if (num_ligas >= count) {
+        // drop the trailing liga's from this set and all subsequent liga sets
+        unsigned num_ligas_to_remove = num_ligas - count;
+        new_liga_set_count = i + 1;
+        c.graph.vertices_[liga_set.index].obj.tail -= num_ligas_to_remove * SmallTypes::size;
+        liga_set.table->ligature.len = count;
+        break;
+      } else {
+        count -= num_ligas;
+      }
+    }
+
+    // Adjust liga set array
+    c.graph.vertices_[this_index].obj.tail -= (ligatureSet.len - new_liga_set_count) * SmallTypes::size;
+    ligatureSet.len = new_liga_set_count;
+
+    // Coverage matches the number of liga sets so rebuild as needed
+    auto coverage = c.graph.as_mutable_table<Coverage> (this_index, &this->coverage);
+    if (!coverage) return false;
+
+    for (unsigned i : retained_indices.iter())
+      add_virtual_link(c, i, coverage.index);
+
+    unsigned coverage_size = coverage.vertex->table_size ();
+    auto new_coverage =
+        + hb_zip (coverage.table->iter (), hb_range ())
+        | hb_filter ([&] (hb_pair_t<unsigned, unsigned> p) {
+          return p.second < new_liga_set_count;
+        })
+        | hb_map_retains_sorting (hb_first)
+        ;
+
+    return Coverage::make_coverage (c, new_coverage, coverage.index, coverage_size);
+  }
+};
+
+struct LigatureSubst : public OT::Layout::GSUB_impl::LigatureSubst
+{
+
+  hb_vector_t<unsigned> split_subtables (gsubgpos_graph_context_t& c,
+                                         unsigned parent_index,
+                                         unsigned this_index)
+  {
+    switch (u.format) {
+    case 1:
+      return ((LigatureSubstFormat1*)(&u.format1))->split_subtables (c, parent_index, this_index);
+#ifndef HB_NO_BEYOND_64K
+    case 2: HB_FALLTHROUGH;
+      // Don't split 24bit Ligature Subs
+#endif
+    default:
+      return hb_vector_t<unsigned> ();
+    }
+  }
+
+  bool sanitize (graph_t::vertex_t& vertex) const
+  {
+    int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
+    if (vertex_len < u.format.get_size ()) return false;
+    hb_barrier ();
+
+    switch (u.format) {
+    case 1:
+      return ((LigatureSubstFormat1*)(&u.format1))->sanitize (vertex);
+#ifndef HB_NO_BEYOND_64K
+    case 2:  HB_FALLTHROUGH;
+#endif
+    default:
+      // We don't handle format 2 here.
+      return false;
+    }
+  }
+};
+
+}
+
+#endif  // GRAPH_LIGATURE_GRAPH_HH

+ 1 - 1
thirdparty/harfbuzz/src/graph/pairpos-graph.hh

@@ -423,7 +423,7 @@ struct PairPosFormat2 : public OT::Layout::GPOS_impl::PairPosFormat2_4<SmallType
     class_def_link->width = SmallTypes::size;
     class_def_link->objidx = class_def_2_id;
     class_def_link->position = 10;
-    graph.vertices_[class_def_2_id].add_parent (pair_pos_prime_id);
+    graph.vertices_[class_def_2_id].add_parent (pair_pos_prime_id, false);
     graph.duplicate (pair_pos_prime_id, class_def_2_id);
 
     return pair_pos_prime_id;

+ 10 - 6
thirdparty/harfbuzz/src/graph/serialize.hh

@@ -172,8 +172,11 @@ void print_overflows (graph_t& graph,
 template <typename O> inline void
 serialize_link_of_type (const hb_serialize_context_t::object_t::link_t& link,
                         char* head,
+                        unsigned size,
                         hb_serialize_context_t* c)
 {
+  assert(link.position + link.width <= size);
+
   OT::Offset<O>* offset = reinterpret_cast<OT::Offset<O>*> (head + link.position);
   *offset = 0;
   c->add_link (*offset,
@@ -187,6 +190,7 @@ serialize_link_of_type (const hb_serialize_context_t::object_t::link_t& link,
 inline
 void serialize_link (const hb_serialize_context_t::object_t::link_t& link,
                      char* head,
+                     unsigned size,
                      hb_serialize_context_t* c)
 {
   switch (link.width)
@@ -197,21 +201,21 @@ void serialize_link (const hb_serialize_context_t::object_t::link_t& link,
     case 4:
       if (link.is_signed)
       {
-        serialize_link_of_type<OT::HBINT32> (link, head, c);
+        serialize_link_of_type<OT::HBINT32> (link, head, size, c);
       } else {
-        serialize_link_of_type<OT::HBUINT32> (link, head, c);
+        serialize_link_of_type<OT::HBUINT32> (link, head, size, c);
       }
       return;
     case 2:
       if (link.is_signed)
       {
-        serialize_link_of_type<OT::HBINT16> (link, head, c);
+        serialize_link_of_type<OT::HBINT16> (link, head, size, c);
       } else {
-        serialize_link_of_type<OT::HBUINT16> (link, head, c);
+        serialize_link_of_type<OT::HBUINT16> (link, head, size, c);
       }
       return;
     case 3:
-      serialize_link_of_type<OT::HBUINT24> (link, head, c);
+      serialize_link_of_type<OT::HBUINT24> (link, head, size, c);
       return;
     default:
       // Unexpected link width.
@@ -251,7 +255,7 @@ inline hb_blob_t* serialize (const graph_t& graph)
 
     // Only real links needs to be serialized.
     for (const auto& link : vertices[i].obj.real_links)
-      serialize_link (link, start, &c);
+      serialize_link (link, start, size, &c);
 
     // All duplications are already encoded in the graph, so don't
     // enable sharing during packing.

+ 183 - 70
thirdparty/harfbuzz/src/hb-algs.hh

@@ -78,129 +78,220 @@
 
 
 /*
- * Big-endian integers.
+ * Fixed-endian integers / floats.
  */
 
+
 /* Endian swap, used in Windows related backends */
 static inline constexpr uint16_t hb_uint16_swap (uint16_t v)
 { return (v >> 8) | (v << 8); }
 static inline constexpr uint32_t hb_uint32_swap (uint32_t v)
 { return (hb_uint16_swap (v) << 16) | hb_uint16_swap (v >> 16); }
 
-#ifndef HB_FAST_INT_ACCESS
+template <typename Type>
+struct __attribute__((packed)) hb_packed_t { Type v; };
+
+#ifndef HB_FAST_NUM_ACCESS
 #if defined(__OPTIMIZE__) && \
     defined(__BYTE_ORDER) && \
     (__BYTE_ORDER == __BIG_ENDIAN || \
      (__BYTE_ORDER == __LITTLE_ENDIAN && \
       hb_has_builtin(__builtin_bswap16) && \
       hb_has_builtin(__builtin_bswap32)))
-#define HB_FAST_INT_ACCESS 1
+#define HB_FAST_NUM_ACCESS 1
 #else
-#define HB_FAST_INT_ACCESS 0
+#define HB_FAST_NUM_ACCESS 0
 #endif
 #endif
 
-template <typename Type, int Bytes = sizeof (Type)>
-struct BEInt;
-template <typename Type>
-struct BEInt<Type, 1>
+template <bool BE, typename Type, int Bytes = sizeof (Type)>
+struct HBInt;
+template <bool BE, typename Type>
+struct HBInt<BE, Type, 1>
 {
   public:
-  BEInt () = default;
-  constexpr BEInt (Type V) : v {uint8_t (V)} {}
+  HBInt () = default;
+  constexpr HBInt (Type V) : v {uint8_t (V)} {}
   constexpr operator Type () const { return v; }
   private: uint8_t v;
 };
-template <typename Type>
-struct BEInt<Type, 2>
+template <bool BE, typename Type>
+struct HBInt<BE, Type, 2>
 {
-  struct __attribute__((packed)) packed_uint16_t { uint16_t v; };
-
   public:
-  BEInt () = default;
-
-  BEInt (Type V)
-#if HB_FAST_INT_ACCESS
-#if __BYTE_ORDER == __LITTLE_ENDIAN
-  { ((packed_uint16_t *) v)->v = __builtin_bswap16 (V); }
-#else /* __BYTE_ORDER == __BIG_ENDIAN */
-  { ((packed_uint16_t *) v)->v = V; }
-#endif
+  HBInt () = default;
+
+  HBInt (Type V)
+#if HB_FAST_NUM_ACCESS
+  {
+    if (BE == (__BYTE_ORDER == __BIG_ENDIAN))
+      ((hb_packed_t<uint16_t> *) v)->v = V;
+    else
+      ((hb_packed_t<uint16_t> *) v)->v = __builtin_bswap16 (V);
+  }
 #else
-    : v {uint8_t ((V >>  8) & 0xFF),
-	 uint8_t ((V      ) & 0xFF)} {}
+    : v {BE ? uint8_t ((V >>  8) & 0xFF) : uint8_t ((V      ) & 0xFF),
+	 BE ? uint8_t ((V      ) & 0xFF) : uint8_t ((V >>  8) & 0xFF)} {}
 #endif
 
-  constexpr operator Type () const {
-#if HB_FAST_INT_ACCESS
-#if __BYTE_ORDER == __LITTLE_ENDIAN
-    return __builtin_bswap16 (((packed_uint16_t *) v)->v);
-#else /* __BYTE_ORDER == __BIG_ENDIAN */
-    return ((packed_uint16_t *) v)->v;
-#endif
+  constexpr operator Type () const
+  {
+#if HB_FAST_NUM_ACCESS
+    return (BE == (__BYTE_ORDER == __BIG_ENDIAN)) ?
+      ((const hb_packed_t<uint16_t> *) v)->v
+    :
+      __builtin_bswap16 (((const hb_packed_t<uint16_t> *) v)->v)
+    ;
 #else
-    return (v[0] <<  8)
-	 + (v[1]      );
+    return (BE ? (v[0] <<  8) : (v[0]      ))
+	 + (BE ? (v[1]      ) : (v[1] <<  8));
 #endif
   }
   private: uint8_t v[2];
 };
-template <typename Type>
-struct BEInt<Type, 3>
+template <bool BE, typename Type>
+struct HBInt<BE, Type, 3>
 {
   static_assert (!std::is_signed<Type>::value, "");
   public:
-  BEInt () = default;
-  constexpr BEInt (Type V) : v {uint8_t ((V >> 16) & 0xFF),
-				uint8_t ((V >>  8) & 0xFF),
-				uint8_t ((V      ) & 0xFF)} {}
-
-  constexpr operator Type () const { return (v[0] << 16)
-					  + (v[1] <<  8)
-					  + (v[2]      ); }
+  HBInt () = default;
+  constexpr HBInt (Type V) : v {BE ? uint8_t ((V >> 16) & 0xFF) : uint8_t ((V >> 16) & 0xFF),
+				BE ? uint8_t ((V >>  8) & 0xFF) : uint8_t ((V >>  8) & 0xFF),
+				BE ? uint8_t ((V      ) & 0xFF) : uint8_t ((V      ) & 0xFF)} {}
+
+  constexpr operator Type () const { return (BE ? (v[0] << 16) : (v[0]      ))
+					  + (BE ? (v[1] <<  8) : (v[1] <<  8))
+					  + (BE ? (v[2]      ) : (v[2] << 16)); }
   private: uint8_t v[3];
 };
-template <typename Type>
-struct BEInt<Type, 4>
+template <bool BE, typename Type>
+struct HBInt<BE, Type, 4>
 {
-  struct __attribute__((packed)) packed_uint32_t { uint32_t v; };
+  template <bool, typename, int>
+  friend struct HBFloat;
 
   public:
-  BEInt () = default;
-
-  BEInt (Type V)
-#if HB_FAST_INT_ACCESS
-#if __BYTE_ORDER == __LITTLE_ENDIAN
-  { ((packed_uint32_t *) v)->v = __builtin_bswap32 (V); }
-#else /* __BYTE_ORDER == __BIG_ENDIAN */
-  { ((packed_uint32_t *) v)->v = V; }
-#endif
+  HBInt () = default;
+
+  HBInt (Type V)
+#if HB_FAST_NUM_ACCESS
+  {
+    if (BE == (__BYTE_ORDER == __BIG_ENDIAN))
+      ((hb_packed_t<uint32_t> *) v)->v = V;
+    else
+      ((hb_packed_t<uint32_t> *) v)->v = __builtin_bswap32 (V);
+  }
 #else
-    : v {uint8_t ((V >> 24) & 0xFF),
-	 uint8_t ((V >> 16) & 0xFF),
-	 uint8_t ((V >>  8) & 0xFF),
-	 uint8_t ((V      ) & 0xFF)} {}
+    : v {BE ? uint8_t ((V >> 24) & 0xFF) : uint8_t ((V      ) & 0xFF),
+	 BE ? uint8_t ((V >> 16) & 0xFF) : uint8_t ((V >>  8) & 0xFF),
+	 BE ? uint8_t ((V >>  8) & 0xFF) : uint8_t ((V >> 16) & 0xFF),
+	 BE ? uint8_t ((V      ) & 0xFF) : uint8_t ((V >> 24) & 0xFF)} {}
 #endif
 
   constexpr operator Type () const {
-#if HB_FAST_INT_ACCESS
-#if __BYTE_ORDER == __LITTLE_ENDIAN
-    return __builtin_bswap32 (((packed_uint32_t *) v)->v);
-#else /* __BYTE_ORDER == __BIG_ENDIAN */
-    return ((packed_uint32_t *) v)->v;
-#endif
+#if HB_FAST_NUM_ACCESS
+    return (BE == (__BYTE_ORDER == __BIG_ENDIAN)) ?
+      ((const hb_packed_t<uint32_t> *) v)->v
+    :
+      __builtin_bswap32 (((const hb_packed_t<uint32_t> *) v)->v)
+    ;
 #else
-    return (v[0] << 24)
-	 + (v[1] << 16)
-	 + (v[2] <<  8)
-	 + (v[3]      );
+    return (BE ? (v[0] << 24) : (v[0]      ))
+	 + (BE ? (v[1] << 16) : (v[1] <<  8))
+	 + (BE ? (v[2] <<  8) : (v[2] << 16))
+	 + (BE ? (v[3]      ) : (v[3] << 24));
 #endif
   }
   private: uint8_t v[4];
 };
+template <bool BE, typename Type>
+struct HBInt<BE, Type, 8>
+{
+  template <bool, typename, int>
+  friend struct HBFloat;
+
+  public:
+  HBInt () = default;
+
+  HBInt (Type V)
+    : v {BE ? uint8_t ((V >> 56) & 0xFF) : uint8_t ((V      ) & 0xFF),
+	 BE ? uint8_t ((V >> 48) & 0xFF) : uint8_t ((V >>  8) & 0xFF),
+	 BE ? uint8_t ((V >> 40) & 0xFF) : uint8_t ((V >> 16) & 0xFF),
+	 BE ? uint8_t ((V >> 32) & 0xFF) : uint8_t ((V >> 24) & 0xFF),
+	 BE ? uint8_t ((V >> 24) & 0xFF) : uint8_t ((V >> 32) & 0xFF),
+	 BE ? uint8_t ((V >> 16) & 0xFF) : uint8_t ((V >> 40) & 0xFF),
+	 BE ? uint8_t ((V >>  8) & 0xFF) : uint8_t ((V >> 48) & 0xFF),
+	 BE ? uint8_t ((V      ) & 0xFF) : uint8_t ((V >> 56) & 0xFF)} {}
+
+  constexpr operator Type () const {
+    return (BE ? (uint64_t (v[0]) << 56) : (uint64_t (v[0])      ))
+	 + (BE ? (uint64_t (v[1]) << 48) : (uint64_t (v[1]) <<  8))
+	 + (BE ? (uint64_t (v[2]) << 40) : (uint64_t (v[2]) << 16))
+	 + (BE ? (uint64_t (v[3]) << 32) : (uint64_t (v[3]) << 24))
+	 + (BE ? (uint64_t (v[4]) << 24) : (uint64_t (v[4]) << 32))
+	 + (BE ? (uint64_t (v[5]) << 16) : (uint64_t (v[5]) << 40))
+	 + (BE ? (uint64_t (v[6]) <<  8) : (uint64_t (v[6]) << 48))
+	 + (BE ? (uint64_t (v[7])      ) : (uint64_t (v[7]) << 56));
+  }
+  private: uint8_t v[8];
+};
 
 /* Floats. */
 
+template <bool BE, typename Type, int Bytes>
+struct HBFloat
+{
+  using IntType = typename std::conditional<Bytes == 4, uint32_t, uint64_t>::type;
+
+  public:
+  HBFloat () = default;
+
+  HBFloat (Type V)
+  {
+#if HB_FAST_NUM_ACCESS
+    {
+      if (BE == (__BYTE_ORDER == __BIG_ENDIAN))
+      {
+        ((hb_packed_t<Type> *) v)->v = V;
+        return;
+      }
+    }
+#endif
+
+    union {
+      hb_packed_t<Type> f;
+      hb_packed_t<IntType> i;
+    } u = {{V}};
+
+    const HBInt<BE, IntType> I = u.i.v;
+    for (unsigned i = 0; i < Bytes; i++)
+      v[i] = I.v[i];
+  }
+
+  /* c++14 constexpr */ operator Type () const
+  {
+#if HB_FAST_NUM_ACCESS
+    {
+      if (BE == (__BYTE_ORDER == __BIG_ENDIAN))
+	return ((const hb_packed_t<Type> *) v)->v;
+    }
+#endif
+
+    HBInt<BE, IntType> I;
+    for (unsigned i = 0; i < Bytes; i++)
+      I.v[i] = v[i];
+
+    union {
+      hb_packed_t<IntType> i;
+      hb_packed_t<Type> f;
+    } u = {{I}};
+
+    return u.f.v;
+  }
+  private: uint8_t v[Bytes];
+};
+
+
 /* We want our rounding towards +infinity. */
 static inline double
 _hb_roundf (double x) { return floor (x + .5); }
@@ -210,6 +301,27 @@ _hb_roundf (float x) { return floorf (x + .5f); }
 
 #define roundf(x) _hb_roundf(x)
 
+static inline void
+hb_sincos (float rotation, float &s, float &c)
+{
+#ifdef HAVE_SINCOSF
+  sincosf (rotation, &s, &c);
+#else
+  c = cosf (rotation);
+  s = sinf (rotation);
+#endif
+}
+static inline void
+hb_sincos (double rotation, double &s, double &c)
+{
+#ifdef HAVE_SINCOS
+  sincos (rotation, &s, &c);
+#else
+  c = cos (rotation);
+  s = sin (rotation);
+#endif
+}
+
 
 /* Encodes three unsigned integers in one 64-bit number.  If the inputs have more than 21 bits,
  * values will be truncated / overlap, and might not decode exactly. */
@@ -1070,6 +1182,7 @@ _hb_cmp_operator (const void *pkey, const void *pval)
 }
 
 template <typename V, typename K, typename ...Ts>
+HB_HOT
 static inline bool
 hb_bsearch_impl (unsigned *pos, /* Out */
 		 const K& key,

+ 4 - 3
thirdparty/harfbuzz/src/hb-cache.hh

@@ -83,6 +83,7 @@ struct hb_cache_t
       v = -1;
   }
 
+  HB_HOT
   bool get (unsigned int key, unsigned int *value) const
   {
     unsigned int k = key & ((1u<<cache_bits)-1);
@@ -94,14 +95,14 @@ struct hb_cache_t
     return true;
   }
 
-  bool set (unsigned int key, unsigned int value)
+  HB_HOT
+  void set (unsigned int key, unsigned int value)
   {
     if (unlikely ((key >> key_bits) || (value >> value_bits)))
-      return false; /* Overflows */
+      return; /* Overflows */
     unsigned int k = key & ((1u<<cache_bits)-1);
     unsigned int v = ((key>>cache_bits)<<value_bits) | value;
     values[k] = v;
-    return true;
   }
 
   private:

+ 1 - 1
thirdparty/harfbuzz/src/hb-cff2-interp-cs.hh

@@ -77,7 +77,7 @@ struct cff2_cs_interp_env_t : cs_interp_env_t<ELEM, CFF2Subrs>
     coords = coords_;
     num_coords = num_coords_;
     varStore = acc.varStore;
-    do_blend = num_coords && coords && varStore->size;
+    do_blend = num_coords && varStore->size;
     set_ivs (acc.privateDicts[fd].ivs);
   }
 

+ 5 - 2
thirdparty/harfbuzz/src/hb-common.cc

@@ -545,8 +545,11 @@ hb_script_to_iso15924_tag (hb_script_t script)
  * Fetches the #hb_direction_t of a script when it is
  * set horizontally. All right-to-left scripts will return
  * #HB_DIRECTION_RTL. All left-to-right scripts will return
- * #HB_DIRECTION_LTR.  Scripts that can be written either
- * horizontally or vertically will return #HB_DIRECTION_INVALID.
+ * #HB_DIRECTION_LTR.
+ *
+ * Scripts that can be written either right-to-left or
+ * left-to-right will return #HB_DIRECTION_INVALID.
+ *
  * Unknown scripts will return #HB_DIRECTION_LTR.
  *
  * Return value: The horizontal #hb_direction_t of @script

+ 4 - 7
thirdparty/harfbuzz/src/hb-coretext-shape.cc

@@ -73,9 +73,7 @@ _hb_coretext_shaper_font_data_create (hb_font_t *font)
     return nullptr;
   }
 
-  unsigned num_axes = hb_ot_var_get_axis_count (face);
-  // https://github.com/harfbuzz/harfbuzz/issues/5163
-  if (num_axes)
+  if (font->num_coords)
   {
     CFMutableDictionaryRef variations =
       CFDictionaryCreateMutable (kCFAllocatorDefault,
@@ -83,15 +81,14 @@ _hb_coretext_shaper_font_data_create (hb_font_t *font)
 				 &kCFTypeDictionaryKeyCallBacks,
 				 &kCFTypeDictionaryValueCallBacks);
 
-    unsigned count = hb_max (num_axes, font->num_coords);
+    unsigned count = font->num_coords;
     for (unsigned i = 0; i < count; i++)
     {
       hb_ot_var_axis_info_t info;
       unsigned int c = 1;
       hb_ot_var_get_axis_infos (font->face, i, &c, &info);
-      float v = i < font->num_coords ?
-		hb_clamp (font->design_coords[i], info.min_value, info.max_value) :
-		info.default_value;
+
+      float v = hb_clamp (font->design_coords[i], info.min_value, info.max_value);
 
       CFNumberRef tag_number = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &info.tag);
       CFNumberRef value_number = CFNumberCreate (kCFAllocatorDefault, kCFNumberFloatType, &v);

+ 2 - 1
thirdparty/harfbuzz/src/hb-coretext.cc

@@ -206,8 +206,9 @@ create_cg_font (hb_blob_t *blob, unsigned int index)
   if (unlikely (named_instance_index != 0))
   {
     // https://github.com/harfbuzz/harfbuzz/issues/5300
+    // https://github.com/harfbuzz/harfbuzz/issues/5354
 #if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000) || \
-    (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300) || \
+    (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500) || \
     (defined(__TV_OS_VERSION_MIN_REQUIRED) && __TV_OS_VERSION_MIN_REQUIRED >= 110000) || \
     (defined(__WATCH_OS_VERSION_MIN_REQUIRED) && __WATCH_OS_VERSION_MIN_REQUIRED >= 40000) || \
     (defined(__MACCATALYST_VERSION_MIN_REQUIRED) && __MACCATALYST_VERSION_MIN_REQUIRED >= 130100) || \

+ 4 - 4
thirdparty/harfbuzz/src/hb-deprecated.h

@@ -287,7 +287,7 @@ typedef void (*hb_font_get_glyph_shape_func_t) (hb_font_t *font, void *font_data
  * A virtual method for the #hb_font_funcs_t of an #hb_font_t object.
  *
  * Since: 7.0.0
- * XDeprecated: REPLACEME: Use hb_font_draw_glyph_func_or_fail_t instead.
+ * Deprecated: 11.2.0: Use hb_font_draw_glyph_func_or_fail_t instead.
  **/
 typedef void (*hb_font_draw_glyph_func_t) (hb_font_t *font, void *font_data,
                                            hb_codepoint_t glyph,
@@ -308,7 +308,7 @@ typedef void (*hb_font_draw_glyph_func_t) (hb_font_t *font, void *font_data,
  * A virtual method for the #hb_font_funcs_t of an #hb_font_t object.
  *
  * Since: 7.0.0
- * XDeprecated: REPLACEME: Use hb_font_paint_glyph_or_fail_func_t instead.
+ * Deprecated: 11.2.0: Use hb_font_paint_glyph_or_fail_func_t instead.
  */
 typedef hb_bool_t (*hb_font_paint_glyph_func_t) (hb_font_t *font, void *font_data,
 						 hb_codepoint_t glyph,
@@ -346,7 +346,7 @@ hb_font_funcs_set_glyph_shape_func (hb_font_funcs_t *ffuncs,
  * Sets the implementation function for #hb_font_draw_glyph_func_t.
  *
  * Since: 7.0.0
- * XDeprecated: REPLACEME: Use hb_font_funcs_set_draw_glyph_or_fail_func instead.
+ * Deprecated: 11.2.0: Use hb_font_funcs_set_draw_glyph_or_fail_func instead.
  **/
 HB_DEPRECATED_FOR (hb_font_funcs_set_draw_glyph_or_fail_func)
 HB_EXTERN void
@@ -364,7 +364,7 @@ hb_font_funcs_set_draw_glyph_func (hb_font_funcs_t *ffuncs,
  * Sets the implementation function for #hb_font_paint_glyph_func_t.
  *
  * Since: 7.0.0
- * XDeprecated: REPLACEME: Use hb_font_funcs_set_paint_glyph_or_fail_func() instead.
+ * Deprecated: 11.2.0: Use hb_font_funcs_set_paint_glyph_or_fail_func() instead.
  */
 HB_DEPRECATED_FOR (hb_font_funcs_set_paint_glyph_or_fail_func)
 HB_EXTERN void

+ 55 - 0
thirdparty/harfbuzz/src/hb-directwrite.cc

@@ -161,6 +161,58 @@ _hb_directwrite_table_data_release (void *data)
   hb_free (context);
 }
 
+static hb_blob_t *
+_hb_directwrite_get_file_blob (IDWriteFontFace *dw_face)
+{
+  UINT32 file_count;
+  if (FAILED (dw_face->GetFiles(&file_count, NULL)))
+    return nullptr;
+
+  IDWriteFontFile **files = new IDWriteFontFile*[file_count];
+  if (FAILED (dw_face->GetFiles(&file_count, files)))
+  {
+    delete [] files;
+    return nullptr;
+  }
+
+  hb_blob_t *blob = nullptr;
+  for (UINT32 i = 0; i < file_count; i++)
+  {
+    LPCVOID reference_key;
+    UINT32 reference_key_size;
+    if (FAILED (files[i]->GetReferenceKey(&reference_key, &reference_key_size)))
+      continue;
+
+    IDWriteFontFileLoader *loader;
+    if (FAILED (files[i]->GetLoader(&loader)))
+      continue;
+    
+    IDWriteFontFileStream *stream;
+    if (FAILED (loader->CreateStreamFromKey (reference_key, reference_key_size, &stream)))
+    {
+      loader->Release ();
+      continue;
+    }
+
+    UINT64 file_size;
+    const void *fragment;
+    void *context;
+    if (FAILED (stream->GetFileSize(&file_size)) ||
+        FAILED (stream->ReadFileFragment (&fragment, 0, file_size, &context)))
+    {
+      loader->Release ();
+      continue;
+    }
+    blob = hb_blob_create ((const char *) fragment, file_size, HB_MEMORY_MODE_DUPLICATE, NULL, NULL);
+    stream->ReleaseFileFragment (context);
+    loader->Release ();
+    break;
+  }
+
+  delete [] files;
+  return blob;
+}
+
 static hb_blob_t *
 _hb_directwrite_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data)
 {
@@ -169,6 +221,9 @@ _hb_directwrite_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *
   uint32_t length;
   void *table_context;
   BOOL exists;
+  if (tag == HB_TAG_NONE)
+    return _hb_directwrite_get_file_blob (dw_face);
+
   if (FAILED (dw_face->TryGetFontTable (hb_uint32_swap (tag), &data,
 					&length, &table_context, &exists)))
     return nullptr;

+ 11 - 11
thirdparty/harfbuzz/src/hb-draw.cc

@@ -63,14 +63,14 @@ hb_draw_quadratic_to_nil (hb_draw_funcs_t *dfuncs, void *draw_data,
 			  float to_x, float to_y,
 			  void *user_data HB_UNUSED)
 {
-#define HB_ONE_THIRD 0.33333333f
+#define HB_TWO_THIRD 0.66666666666666666666666667f
   dfuncs->emit_cubic_to (draw_data, *st,
-			 (st->current_x + 2.f * control_x) * HB_ONE_THIRD,
-			 (st->current_y + 2.f * control_y) * HB_ONE_THIRD,
-			 (to_x + 2.f * control_x) * HB_ONE_THIRD,
-			 (to_y + 2.f * control_y) * HB_ONE_THIRD,
+			 st->current_x + (control_x - st->current_x) * HB_TWO_THIRD,
+			 st->current_y + (control_y - st->current_y) * HB_TWO_THIRD,
+			 to_x + (control_x - to_x) * HB_TWO_THIRD,
+			 to_y + (control_y - to_y) * HB_TWO_THIRD,
 			 to_x, to_y);
-#undef HB_ONE_THIRD
+#undef HB_TWO_THIRD
 }
 
 static void
@@ -277,7 +277,7 @@ hb_draw_funcs_destroy (hb_draw_funcs_t *dfuncs)
  * @destroy: (nullable): A callback to call when @data is not needed anymore
  * @replace: Whether to replace an existing data with the same key
  *
- * Attaches a user-data key/data pair to the specified draw-functions structure. 
+ * Attaches a user-data key/data pair to the specified draw-functions structure.
  *
  * Return value: `true` if success, `false` otherwise
  *
@@ -467,7 +467,7 @@ hb_draw_extents_move_to (hb_draw_funcs_t *dfuncs HB_UNUSED,
 			 float to_x, float to_y,
 			 void *user_data HB_UNUSED)
 {
-  hb_extents_t *extents = (hb_extents_t *) data;
+  hb_extents_t<> *extents = (hb_extents_t<> *) data;
 
   extents->add_point (to_x, to_y);
 }
@@ -479,7 +479,7 @@ hb_draw_extents_line_to (hb_draw_funcs_t *dfuncs HB_UNUSED,
 			 float to_x, float to_y,
 			 void *user_data HB_UNUSED)
 {
-  hb_extents_t *extents = (hb_extents_t *) data;
+  hb_extents_t<> *extents = (hb_extents_t<> *) data;
 
   extents->add_point (to_x, to_y);
 }
@@ -492,7 +492,7 @@ hb_draw_extents_quadratic_to (hb_draw_funcs_t *dfuncs HB_UNUSED,
 			      float to_x, float to_y,
 			      void *user_data HB_UNUSED)
 {
-  hb_extents_t *extents = (hb_extents_t *) data;
+  hb_extents_t<> *extents = (hb_extents_t<> *) data;
 
   extents->add_point (control_x, control_y);
   extents->add_point (to_x, to_y);
@@ -507,7 +507,7 @@ hb_draw_extents_cubic_to (hb_draw_funcs_t *dfuncs HB_UNUSED,
 			  float to_x, float to_y,
 			  void *user_data HB_UNUSED)
 {
-  hb_extents_t *extents = (hb_extents_t *) data;
+  hb_extents_t<> *extents = (hb_extents_t<> *) data;
 
   extents->add_point (control1_x, control1_y);
   extents->add_point (control2_x, control2_y);

+ 1 - 2
thirdparty/harfbuzz/src/hb-face-builder.cc

@@ -169,8 +169,7 @@ _hb_face_builder_get_table_tags (const hb_face_t *face HB_UNUSED,
 
   if (unlikely (start_offset >= population))
   {
-    if (table_count)
-      *table_count = 0;
+    *table_count = 0;
     return population;
   }
 

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

@@ -329,7 +329,7 @@ hb_face_create_from_file_or_fail (const char   *file_name,
   return face;
 }
 
-static struct supported_face_loaders_t {
+static const struct supported_face_loaders_t {
 	char name[16];
 	hb_face_t * (*from_file) (const char *font_file, unsigned face_index);
 	hb_face_t * (*from_blob) (hb_blob_t *blob, unsigned face_index);

+ 211 - 37
thirdparty/harfbuzz/src/hb-font.cc

@@ -246,7 +246,6 @@ hb_font_get_glyph_v_advance_nil (hb_font_t      *font,
 				 hb_codepoint_t  glyph HB_UNUSED,
 				 void           *user_data HB_UNUSED)
 {
-  /* TODO use font_extents.ascender+descender */
   return -font->y_scale;
 }
 
@@ -352,6 +351,10 @@ hb_font_get_glyph_h_origin_default (hb_font_t      *font,
 				    hb_position_t  *y,
 				    void           *user_data HB_UNUSED)
 {
+  if (font->has_glyph_h_origins_func_set ())
+  {
+    return font->get_glyph_h_origins (1, &glyph, 0, x, 0, y, 0, false);
+  }
   hb_bool_t ret = font->parent->get_glyph_h_origin (glyph, x, y);
   if (ret)
     font->parent_scale_position (x, y);
@@ -366,7 +369,6 @@ hb_font_get_glyph_v_origin_nil (hb_font_t      *font HB_UNUSED,
 				hb_position_t  *y,
 				void           *user_data HB_UNUSED)
 {
-  *x = *y = 0;
   return false;
 }
 
@@ -378,12 +380,100 @@ hb_font_get_glyph_v_origin_default (hb_font_t      *font,
 				    hb_position_t  *y,
 				    void           *user_data HB_UNUSED)
 {
+  if (font->has_glyph_v_origins_func_set ())
+  {
+    return font->get_glyph_v_origins (1, &glyph, 0, x, 0, y, 0, false);
+  }
   hb_bool_t ret = font->parent->get_glyph_v_origin (glyph, x, y);
   if (ret)
     font->parent_scale_position (x, y);
   return ret;
 }
 
+#define hb_font_get_glyph_h_origins_nil hb_font_get_glyph_h_origins_default
+
+static hb_bool_t
+hb_font_get_glyph_h_origins_default (hb_font_t *font HB_UNUSED,
+				     void *font_data HB_UNUSED,
+				     unsigned int count,
+				     const hb_codepoint_t *first_glyph HB_UNUSED,
+				     unsigned glyph_stride HB_UNUSED,
+				     hb_position_t *first_x,
+				     unsigned x_stride,
+				     hb_position_t *first_y,
+				     unsigned y_stride,
+				     void *user_data HB_UNUSED)
+{
+  if (font->has_glyph_h_origin_func_set ())
+  {
+    for (unsigned int i = 0; i < count; i++)
+    {
+      font->get_glyph_h_origin (*first_glyph, first_x, first_y, false);
+      first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (first_glyph, glyph_stride);
+      first_x = &StructAtOffsetUnaligned<hb_position_t> (first_x, x_stride);
+      first_y = &StructAtOffsetUnaligned<hb_position_t> (first_y, y_stride);
+    }
+    return true;
+  }
+
+  hb_bool_t ret = font->parent->get_glyph_h_origins (count,
+						     first_glyph, glyph_stride,
+						     first_x, x_stride,
+						     first_y, y_stride);
+  if (ret)
+  {
+    for (unsigned i = 0; i < count; i++)
+    {
+      font->parent_scale_position (first_x, first_y);
+      first_x = &StructAtOffsetUnaligned<hb_position_t> (first_x, x_stride);
+      first_y = &StructAtOffsetUnaligned<hb_position_t> (first_y, y_stride);
+    }
+  }
+  return ret;
+}
+
+#define hb_font_get_glyph_v_origins_nil hb_font_get_glyph_v_origins_default
+
+static hb_bool_t
+hb_font_get_glyph_v_origins_default (hb_font_t *font HB_UNUSED,
+				     void *font_data HB_UNUSED,
+				     unsigned int count,
+				     const hb_codepoint_t *first_glyph HB_UNUSED,
+				     unsigned glyph_stride HB_UNUSED,
+				     hb_position_t *first_x,
+				     unsigned x_stride,
+				     hb_position_t *first_y,
+				     unsigned y_stride,
+				     void *user_data HB_UNUSED)
+{
+  if (font->has_glyph_v_origin_func_set ())
+  {
+    for (unsigned int i = 0; i < count; i++)
+    {
+      font->get_glyph_v_origin (*first_glyph, first_x, first_y, false);
+      first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (first_glyph, glyph_stride);
+      first_x = &StructAtOffsetUnaligned<hb_position_t> (first_x, x_stride);
+      first_y = &StructAtOffsetUnaligned<hb_position_t> (first_y, y_stride);
+    }
+    return true;
+  }
+
+  hb_bool_t ret = font->parent->get_glyph_v_origins (count,
+						     first_glyph, glyph_stride,
+						     first_x, x_stride,
+						     first_y, y_stride);
+  if (ret)
+  {
+    for (unsigned i = 0; i < count; i++)
+    {
+      font->parent_scale_position (first_x, first_y);
+      first_x = &StructAtOffsetUnaligned<hb_position_t> (first_x, x_stride);
+      first_y = &StructAtOffsetUnaligned<hb_position_t> (first_y, y_stride);
+    }
+  }
+  return ret;
+}
+
 static hb_position_t
 hb_font_get_glyph_h_kerning_nil (hb_font_t      *font HB_UNUSED,
 				 void           *font_data HB_UNUSED,
@@ -1256,6 +1346,77 @@ hb_font_get_glyph_v_origin (hb_font_t      *font,
   return font->get_glyph_v_origin (glyph, x, y);
 }
 
+/**
+ * hb_font_get_glyph_h_origins:
+ * @font: #hb_font_t to work upon
+ * @count: The number of glyph IDs in the sequence queried
+ * @first_glyph: The first glyph ID to query
+ * @glyph_stride: The stride between successive glyph IDs
+ * @first_x: (out): The first X coordinate of the origin retrieved
+ * @x_stride: The stride between successive X coordinates
+ * @first_y: (out): The first Y coordinate of the origin retrieved
+ * @y_stride: The stride between successive Y coordinates
+ *
+ * Fetches the (X,Y) coordinates of the origin for requested glyph IDs
+ * in the specified font, for horizontal text segments.
+ *
+ * Return value: `true` if data found, `false` otherwise
+ *
+ * Since: 11.3.0
+ **/
+hb_bool_t
+hb_font_get_glyph_h_origins (hb_font_t      *font,
+			     unsigned int    count,
+			     const hb_codepoint_t *first_glyph,
+			     unsigned int    glyph_stride,
+			     hb_position_t  *first_x,
+			     unsigned int    x_stride,
+			     hb_position_t  *first_y,
+			     unsigned int    y_stride)
+
+{
+  return font->get_glyph_h_origins (count,
+				    first_glyph, glyph_stride,
+				    first_x, x_stride,
+				    first_y, y_stride);
+}
+
+/**
+ * hb_font_get_glyph_v_origins:
+ * @font: #hb_font_t to work upon
+ * @count: The number of glyph IDs in the sequence queried
+ * @first_glyph: The first glyph ID to query
+ * @glyph_stride: The stride between successive glyph IDs
+ * @first_x: (out): The first X coordinate of the origin retrieved
+ * @x_stride: The stride between successive X coordinates
+ * @first_y: (out): The first Y coordinate of the origin retrieved
+ * @y_stride: The stride between successive Y coordinates
+ *
+ * Fetches the (X,Y) coordinates of the origin for requested glyph IDs
+ * in the specified font, for vertical text segments.
+ *
+ * Return value: `true` if data found, `false` otherwise
+ *
+ * Since: 11.3.0
+ **/
+hb_bool_t
+hb_font_get_glyph_v_origins (hb_font_t      *font,
+			     unsigned int    count,
+			     const hb_codepoint_t *first_glyph,
+			     unsigned int    glyph_stride,
+			     hb_position_t  *first_x,
+			     unsigned int    x_stride,
+			     hb_position_t  *first_y,
+			     unsigned int    y_stride)
+
+{
+  return font->get_glyph_v_origins (count,
+				    first_glyph, glyph_stride,
+				    first_x, x_stride,
+				    first_y, y_stride);
+}
+
+
 /**
  * hb_font_get_glyph_h_kerning:
  * @font: #hb_font_t to work upon
@@ -1443,7 +1604,7 @@ hb_font_get_glyph_shape (hb_font_t *font,
  *
  * Return value: `true` if glyph was drawn, `false` otherwise
  *
- * XSince: REPLACEME
+ * Since: 11.2.0
  **/
 hb_bool_t
 hb_font_draw_glyph_or_fail (hb_font_t *font,
@@ -1480,7 +1641,7 @@ hb_font_draw_glyph_or_fail (hb_font_t *font,
  *
  * Return value: `true` if glyph was painted, `false` otherwise
  *
- * XSince: REPLACEME
+ * Since: 11.2.0
  */
 hb_bool_t
 hb_font_paint_glyph_or_fail (hb_font_t *font,
@@ -1883,6 +2044,7 @@ DEFINE_NULL_INSTANCE (hb_font_t) =
 
   1000, /* x_scale */
   1000, /* y_scale */
+  false, /* is_synthetic */
   0.f, /* x_embolden */
   0.f, /* y_embolden */
   true, /* embolden_in_place */
@@ -1900,6 +2062,7 @@ DEFINE_NULL_INSTANCE (hb_font_t) =
   0, /* ptem */
 
   HB_FONT_NO_VAR_NAMED_INSTANCE, /* instance_index */
+  false, /* has_nonzero_coords */
   0, /* num_coords */
   nullptr, /* coords */
   nullptr, /* design_coords */
@@ -1960,8 +2123,14 @@ hb_font_create (hb_face_t *face)
   hb_font_set_funcs_using (font, nullptr);
 
 #ifndef HB_NO_VAR
-  if (face && face->index >> 16)
-    hb_font_set_var_named_instance (font, (face->index >> 16) - 1);
+  // Initialize variations.
+  if (likely (face))
+  {
+    if (face->index >> 16)
+      hb_font_set_var_named_instance (font, (face->index >> 16) - 1);
+    else
+      hb_font_set_variations (font, nullptr, 0);
+  }
 #endif
 
   return font;
@@ -1979,6 +2148,7 @@ _hb_font_adopt_var_coords (hb_font_t *font,
   font->coords = coords;
   font->design_coords = design_coords;
   font->num_coords = coords_length;
+  font->has_nonzero_coords = hb_any (hb_array (coords, coords_length));
 
   font->changed ();
   font->serial_coords = font->serial;
@@ -2393,7 +2563,7 @@ hb_font_set_funcs_data (hb_font_t         *font,
   font->changed ();
 }
 
-static struct supported_font_funcs_t {
+static const struct supported_font_funcs_t {
 	char name[16];
 	void (*func) (hb_font_t *);
 } supported_font_funcs[] =
@@ -2450,6 +2620,9 @@ hb_bool_t
 hb_font_set_funcs_using (hb_font_t  *font,
 			 const char *name)
 {
+  if (unlikely (hb_object_is_immutable (font)))
+    return false;
+
   bool retry = false;
 
   if (!name || !*name)
@@ -2704,12 +2877,12 @@ hb_font_get_ptem (hb_font_t *font)
  *
  * Return value: `true` if the font is synthetic, `false` otherwise.
  *
- * XSince: REPLACEME
+ * Since: 11.2.0
  */
 hb_bool_t
 hb_font_is_synthetic (hb_font_t *font)
 {
-  return font->is_synthetic ();
+  return font->is_synthetic;
 }
 
 /**
@@ -2858,12 +3031,6 @@ hb_font_set_variations (hb_font_t            *font,
   if (hb_object_is_immutable (font))
     return;
 
-  if (!variations_length && font->instance_index == HB_FONT_NO_VAR_NAMED_INSTANCE)
-  {
-    hb_font_set_var_coords_normalized (font, nullptr, 0);
-    return;
-  }
-
   const OT::fvar &fvar = *font->face->table.fvar;
   auto axes = fvar.get_axes ();
   const unsigned coords_length = axes.length;
@@ -2970,7 +3137,6 @@ hb_font_set_variation (hb_font_t *font,
 
   hb_ot_var_normalize_coords (font->face, coords_length, design_coords, normalized);
   _hb_font_adopt_var_coords (font, normalized, design_coords, coords_length);
-
 }
 
 /**
@@ -2991,11 +3157,16 @@ hb_font_set_variation (hb_font_t *font,
 void
 hb_font_set_var_coords_design (hb_font_t    *font,
 			       const float  *coords,
-			       unsigned int  coords_length)
+			       unsigned int  input_coords_length)
 {
   if (hb_object_is_immutable (font))
     return;
 
+  const OT::fvar &fvar = *font->face->table.fvar;
+  auto axes = fvar.get_axes ();
+  const unsigned coords_length = axes.length;
+
+  input_coords_length = hb_min (input_coords_length, coords_length);
   int *normalized = coords_length ? (int *) hb_calloc (coords_length, sizeof (int)) : nullptr;
   float *design_coords = coords_length ? (float *) hb_calloc (coords_length, sizeof (float)) : nullptr;
 
@@ -3006,8 +3177,11 @@ hb_font_set_var_coords_design (hb_font_t    *font,
     return;
   }
 
-  if (coords_length)
-    hb_memcpy (design_coords, coords, coords_length * sizeof (font->design_coords[0]));
+  if (input_coords_length)
+    hb_memcpy (design_coords, coords, input_coords_length * sizeof (font->design_coords[0]));
+  // Fill in the rest with default values
+  for (unsigned int i = input_coords_length; i < coords_length; i++)
+    design_coords[i] = axes[i].get_default ();
 
   hb_ot_var_normalize_coords (font->face, coords_length, coords, normalized);
   _hb_font_adopt_var_coords (font, normalized, design_coords, coords_length);
@@ -3072,34 +3246,31 @@ hb_font_get_var_named_instance (hb_font_t *font)
 void
 hb_font_set_var_coords_normalized (hb_font_t    *font,
 				   const int    *coords, /* 2.14 normalized */
-				   unsigned int  coords_length)
+				   unsigned int  input_coords_length)
 {
   if (hb_object_is_immutable (font))
     return;
 
+  const OT::fvar &fvar = *font->face->table.fvar;
+  auto axes = fvar.get_axes ();
+  unsigned coords_length = axes.length;
+
+  input_coords_length = hb_min (input_coords_length, coords_length);
   int *copy = coords_length ? (int *) hb_calloc (coords_length, sizeof (coords[0])) : nullptr;
-  int *unmapped = coords_length ? (int *) hb_calloc (coords_length, sizeof (coords[0])) : nullptr;
   float *design_coords = coords_length ? (float *) hb_calloc (coords_length, sizeof (design_coords[0])) : nullptr;
 
-  if (unlikely (coords_length && !(copy && unmapped && design_coords)))
+  if (unlikely (coords_length && !(copy && design_coords)))
   {
     hb_free (copy);
-    hb_free (unmapped);
     hb_free (design_coords);
     return;
   }
 
-  if (coords_length)
-  {
-    hb_memcpy (copy, coords, coords_length * sizeof (coords[0]));
-    hb_memcpy (unmapped, coords, coords_length * sizeof (coords[0]));
-  }
+  if (input_coords_length)
+    hb_memcpy (copy, coords, input_coords_length * sizeof (coords[0]));
 
-  /* Best effort design coords simulation */
-  font->face->table.avar->unmap_coords (unmapped, coords_length);
   for (unsigned int i = 0; i < coords_length; ++i)
-    design_coords[i] = font->face->table.fvar->unnormalize_axis_value (i, unmapped[i]);
-  hb_free (unmapped);
+    design_coords[i] = NAN;
 
   _hb_font_adopt_var_coords (font, copy, design_coords, coords_length);
 }
@@ -3112,8 +3283,8 @@ hb_font_set_var_coords_normalized (hb_font_t    *font,
  * Fetches the list of normalized variation coordinates currently
  * set on a font.
  *
- * Note that this returned array may only contain values for some
- * (or none) of the axes; omitted axes effectively have zero values.
+ * <note>Note that if no variation coordinates are set, this function may
+ * return %NULL.</note>
  *
  * Return value is valid as long as variation coordinates of the font
  * are not modified.
@@ -3140,9 +3311,12 @@ hb_font_get_var_coords_normalized (hb_font_t    *font,
  * Fetches the list of variation coordinates (in design-space units) currently
  * set on a font.
  *
- * Note that this returned array may only contain values for some
- * (or none) of the axes; omitted axes effectively have their default
- * values.
+ * <note>Note that if no variation coordinates are set, this function may
+ * return %NULL.</note>
+ *
+ * <note>If variations have been set on the font using normalized coordinates
+ * (i.e. via hb_font_set_var_coords_normalized()), the design coordinates will
+ * have NaN (Not a Number) values.</note>
  *
  * Return value is valid as long as variation coordinates of the font
  * are not modified.

+ 120 - 9
thirdparty/harfbuzz/src/hb-font.h

@@ -97,7 +97,7 @@ hb_font_funcs_is_immutable (hb_font_funcs_t *ffuncs);
  * @descender: The depth of typographic descenders.
  * @line_gap: The suggested line-spacing gap.
  *
- * Font-wide extent values, measured in font units.
+ * Font-wide extent values, measured in scaled units.
  *
  * Note that typically @ascender is positive and @descender
  * negative, in coordinate systems that grow up.
@@ -332,7 +332,7 @@ typedef hb_font_get_glyph_advances_func_t hb_font_get_glyph_v_advances_func_t;
  *
  * A virtual method for the #hb_font_funcs_t of an #hb_font_t object.
  *
- * This method should retrieve the (X,Y) coordinates (in font units) of the
+ * This method should retrieve the (X,Y) coordinates (in scaled units) of the
  * origin for a glyph. Each coordinate must be returned in an #hb_position_t
  * output parameter.
  *
@@ -349,7 +349,7 @@ typedef hb_bool_t (*hb_font_get_glyph_origin_func_t) (hb_font_t *font, void *fon
  *
  * A virtual method for the #hb_font_funcs_t of an #hb_font_t object.
  *
- * This method should retrieve the (X,Y) coordinates (in font units) of the
+ * This method should retrieve the (X,Y) coordinates (in scaled units) of the
  * origin for a glyph, for horizontal-direction text segments. Each
  * coordinate must be returned in an #hb_position_t output parameter.
  * 
@@ -361,13 +361,72 @@ typedef hb_font_get_glyph_origin_func_t hb_font_get_glyph_h_origin_func_t;
  *
  * A virtual method for the #hb_font_funcs_t of an #hb_font_t object.
  *
- * This method should retrieve the (X,Y) coordinates (in font units) of the
+ * This method should retrieve the (X,Y) coordinates (in scaled units) of the
  * origin for a glyph, for vertical-direction text segments. Each coordinate
  * must be returned in an #hb_position_t output parameter.
  * 
  **/
 typedef hb_font_get_glyph_origin_func_t hb_font_get_glyph_v_origin_func_t;
 
+/**
+ * hb_font_get_glyph_origins_func_t:
+ * @font: #hb_font_t to work upon
+ * @font_data: @font user data pointer
+ * @first_glyph: The first glyph ID to query
+ * @count: number of glyphs to query
+ * @glyph_stride: The stride between successive glyph IDs
+ * @first_x: (out): The first origin X coordinate retrieved
+ * @x_stride: The stride between successive origin X coordinates
+ * @first_y: (out): The first origin Y coordinate retrieved
+ * @y_stride: The stride between successive origin Y coordinates
+ * @user_data: User data pointer passed by the caller
+ *
+ * A virtual method for the #hb_font_funcs_t of an #hb_font_t object.
+ *
+ * This method should retrieve the (X,Y) coordinates (in scaled units) of the
+ * origin for each requested glyph. Each coordinate value must be returned in
+ * an #hb_position_t in the two output parameters.
+ *
+ * Return value: `true` if data found, `false` otherwise
+ *
+ * Since: 11.3.0
+ **/
+typedef hb_bool_t (*hb_font_get_glyph_origins_func_t) (hb_font_t *font, void *font_data,
+						       unsigned int count,
+						       const hb_codepoint_t *first_glyph,
+						       unsigned glyph_stride,
+						       hb_position_t *first_x,
+						       unsigned x_stride,
+						       hb_position_t *first_y,
+						       unsigned y_stride,
+						       void *user_data);
+
+/**
+ * hb_font_get_glyph_h_origins_func_t:
+ *
+ * A virtual method for the #hb_font_funcs_t of an #hb_font_t object.
+ *
+ * This method should retrieve the (X,Y) coordinates (in scaled units) of the
+ * origin for requested glyph, for horizontal-direction text segments. Each
+ * coordinate must be returned in a the x/y #hb_position_t output parameters.
+ *
+ * Since: 11.3.0
+ **/
+typedef hb_font_get_glyph_origins_func_t hb_font_get_glyph_h_origins_func_t;
+
+/**
+ * hb_font_get_glyph_v_origins_func_t:
+ *
+ * A virtual method for the #hb_font_funcs_t of an #hb_font_t object.
+ *
+ * This method should retrieve the (X,Y) coordinates (in scaled units) of the
+ * origin for requested glyph, for vertical-direction text segments. Each
+ * coordinate must be returned in a the x/y #hb_position_t output parameters.
+ *
+ * Since: 11.3.0
+ **/
+typedef hb_font_get_glyph_origins_func_t hb_font_get_glyph_v_origins_func_t;
+
 /**
  * hb_font_get_glyph_kerning_func_t:
  * @font: #hb_font_t to work upon
@@ -428,7 +487,7 @@ typedef hb_bool_t (*hb_font_get_glyph_extents_func_t) (hb_font_t *font, void *fo
  *
  * A virtual method for the #hb_font_funcs_t of an #hb_font_t object.
  *
- * This method should retrieve the (X,Y) coordinates (in font units) for a
+ * This method should retrieve the (X,Y) coordinates (in scaled units) for a
  * specified contour point in a glyph. Each coordinate must be returned as
  * an #hb_position_t output parameter.
  * 
@@ -498,7 +557,7 @@ typedef hb_bool_t (*hb_font_get_glyph_from_name_func_t) (hb_font_t *font, void *
  *
  * Return value: `true` if glyph was drawn, `false` otherwise
  *
- * XSince: REPLACEME
+ * Since: 11.2.0
  **/
 typedef hb_bool_t (*hb_font_draw_glyph_or_fail_func_t) (hb_font_t *font, void *font_data,
 							hb_codepoint_t glyph,
@@ -520,7 +579,7 @@ typedef hb_bool_t (*hb_font_draw_glyph_or_fail_func_t) (hb_font_t *font, void *f
  *
  * Return value: `true` if glyph was painted, `false` otherwise
  *
- * XSince: REPLACEME
+ * Since: 11.2.0
  */
 typedef hb_bool_t (*hb_font_paint_glyph_or_fail_func_t) (hb_font_t *font, void *font_data,
 							 hb_codepoint_t glyph,
@@ -707,6 +766,38 @@ hb_font_funcs_set_glyph_v_origin_func (hb_font_funcs_t *ffuncs,
 				       hb_font_get_glyph_v_origin_func_t func,
 				       void *user_data, hb_destroy_func_t destroy);
 
+/**
+ * hb_font_funcs_set_glyph_h_origins_func:
+ * @ffuncs: A font-function structure
+ * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign
+ * @user_data: Data to pass to @func
+ * @destroy: (nullable): The function to call when @user_data is not needed anymore
+ *
+ * Sets the implementation function for #hb_font_get_glyph_h_origins_func_t.
+ *
+ * Since: 11.3.0
+ **/
+HB_EXTERN void
+hb_font_funcs_set_glyph_h_origins_func (hb_font_funcs_t *ffuncs,
+					hb_font_get_glyph_h_origins_func_t func,
+					void *user_data, hb_destroy_func_t destroy);
+
+/**
+ * hb_font_funcs_set_glyph_v_origins_func:
+ * @ffuncs: A font-function structure
+ * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign
+ * @user_data: Data to pass to @func
+ * @destroy: (nullable): The function to call when @user_data is not needed anymore
+ *
+ * Sets the implementation function for #hb_font_get_glyph_v_origins_func_t.
+ *
+ * Since: 11.3.0
+ **/
+HB_EXTERN void
+hb_font_funcs_set_glyph_v_origins_func (hb_font_funcs_t *ffuncs,
+					hb_font_get_glyph_v_origins_func_t func,
+					void *user_data, hb_destroy_func_t destroy);
+
 /**
  * hb_font_funcs_set_glyph_h_kerning_func:
  * @ffuncs: A font-function structure
@@ -796,7 +887,7 @@ hb_font_funcs_set_glyph_from_name_func (hb_font_funcs_t *ffuncs,
  *
  * Sets the implementation function for #hb_font_draw_glyph_or_fail_func_t.
  *
- * XSince: REPLACEME
+ * Since: 11.2.0
  **/
 HB_EXTERN void
 hb_font_funcs_set_draw_glyph_or_fail_func (hb_font_funcs_t *ffuncs,
@@ -812,7 +903,7 @@ hb_font_funcs_set_draw_glyph_or_fail_func (hb_font_funcs_t *ffuncs,
  *
  * Sets the implementation function for #hb_font_paint_glyph_or_fail_func_t.
  *
- * XSince: REPLACEME
+ * Since: 11.2.0
  */
 HB_EXTERN void
 hb_font_funcs_set_paint_glyph_or_fail_func (hb_font_funcs_t *ffuncs,
@@ -876,6 +967,26 @@ hb_font_get_glyph_v_origin (hb_font_t *font,
 			    hb_codepoint_t glyph,
 			    hb_position_t *x, hb_position_t *y);
 
+HB_EXTERN hb_bool_t
+hb_font_get_glyph_h_origins (hb_font_t *font,
+			     unsigned int count,
+			     const hb_codepoint_t *first_glyph,
+			     unsigned glyph_stride,
+			     hb_position_t *first_x,
+			     unsigned x_stride,
+			     hb_position_t *first_y,
+			     unsigned y_stride);
+
+HB_EXTERN hb_bool_t
+hb_font_get_glyph_v_origins (hb_font_t *font,
+			     unsigned int count,
+			     const hb_codepoint_t *first_glyph,
+			     unsigned glyph_stride,
+			     hb_position_t *first_x,
+			     unsigned x_stride,
+			     hb_position_t *first_y,
+			     unsigned y_stride);
+
 HB_EXTERN hb_position_t
 hb_font_get_glyph_h_kerning (hb_font_t *font,
 			     hb_codepoint_t left_glyph, hb_codepoint_t right_glyph);

+ 300 - 57
thirdparty/harfbuzz/src/hb-font.hh

@@ -55,6 +55,8 @@
   HB_FONT_FUNC_IMPLEMENT (get_,glyph_v_advances) \
   HB_FONT_FUNC_IMPLEMENT (get_,glyph_h_origin) \
   HB_FONT_FUNC_IMPLEMENT (get_,glyph_v_origin) \
+  HB_FONT_FUNC_IMPLEMENT (get_,glyph_h_origins) \
+  HB_FONT_FUNC_IMPLEMENT (get_,glyph_v_origins) \
   HB_FONT_FUNC_IMPLEMENT (get_,glyph_h_kerning) \
   HB_IF_NOT_DEPRECATED (HB_FONT_FUNC_IMPLEMENT (get_,glyph_v_kerning)) \
   HB_FONT_FUNC_IMPLEMENT (get_,glyph_extents) \
@@ -118,6 +120,8 @@ struct hb_font_t
   int32_t x_scale;
   int32_t y_scale;
 
+  bool is_synthetic;
+
   float x_embolden;
   float y_embolden;
   bool embolden_in_place;
@@ -139,6 +143,7 @@ struct hb_font_t
 
   /* Font variation coordinates. */
   unsigned int instance_index;
+  bool has_nonzero_coords;
   unsigned int num_coords;
   int *coords;
   float *design_coords;
@@ -430,21 +435,135 @@ struct hb_font_t
   }
 
   hb_bool_t get_glyph_h_origin (hb_codepoint_t glyph,
-				hb_position_t *x, hb_position_t *y)
+				hb_position_t *x, hb_position_t *y,
+				bool synthetic = true)
   {
     *x = *y = 0;
-    return klass->get.f.glyph_h_origin (this, user_data,
-					glyph, x, y,
-					!klass->user_data ? nullptr : klass->user_data->glyph_h_origin);
+    bool ret = klass->get.f.glyph_h_origin (this, user_data,
+					    glyph, x, y,
+					    !klass->user_data ? nullptr : klass->user_data->glyph_h_origin);
+
+    if (synthetic && ret)
+    {
+      /* Slant */
+      if (slant_xy)
+	*x += roundf (*y * slant_xy);
+
+      /* Embolden */
+      if (!embolden_in_place)
+      {
+        *x += x_scale < 0 ? -x_strength : x_strength;
+	*y += y_scale < 0 ? -y_strength : y_strength;
+      }
+    }
+
+    return ret;
   }
 
   hb_bool_t get_glyph_v_origin (hb_codepoint_t glyph,
-				hb_position_t *x, hb_position_t *y)
+				hb_position_t *x, hb_position_t *y,
+				bool synthetic = true)
   {
     *x = *y = 0;
-    return klass->get.f.glyph_v_origin (this, user_data,
-					glyph, x, y,
-					!klass->user_data ? nullptr : klass->user_data->glyph_v_origin);
+    bool ret = klass->get.f.glyph_v_origin (this, user_data,
+					    glyph, x, y,
+					    !klass->user_data ? nullptr : klass->user_data->glyph_v_origin);
+
+    if (synthetic && ret)
+    {
+      /* Slant */
+      if (slant_xy)
+	*x += roundf (*y * slant_xy);
+
+      /* Embolden */
+      if (!embolden_in_place)
+      {
+        *x += x_scale < 0 ? -x_strength : x_strength;
+	*y += y_scale < 0 ? -y_strength : y_strength;
+      }
+    }
+
+    return ret;
+  }
+
+  hb_bool_t get_glyph_h_origins (unsigned int count,
+				 const hb_codepoint_t *first_glyph,
+				 unsigned int glyph_stride,
+				 hb_position_t *first_x,
+				 unsigned int x_stride,
+				 hb_position_t *first_y,
+				 unsigned int y_stride,
+				 bool synthetic = true)
+
+  {
+    bool ret = klass->get.f.glyph_h_origins (this, user_data,
+					     count,
+					     first_glyph, glyph_stride,
+					     first_x, x_stride, first_y, y_stride,
+					     !klass->user_data ? nullptr : klass->user_data->glyph_h_origins);
+
+    if (synthetic && ret)
+    {
+      hb_position_t x_shift = x_scale < 0 ? -x_strength : x_strength;
+      hb_position_t y_shift = y_scale < 0 ? -y_strength : y_strength;
+      for (unsigned i = 0; i < count; i++)
+      {
+	/* Slant */
+	if (slant_xy)
+	  *first_x += roundf (*first_y * slant_xy);
+
+	/* Embolden */
+	if (!embolden_in_place)
+	{
+	  *first_x += x_shift;
+	  *first_y += y_shift;
+	}
+      }
+      first_x = &StructAtOffsetUnaligned<hb_position_t> (first_x, x_stride);
+      first_y = &StructAtOffsetUnaligned<hb_position_t> (first_y, y_stride);
+    }
+
+    return ret;
+  }
+
+  hb_bool_t get_glyph_v_origins (unsigned int count,
+				 const hb_codepoint_t *first_glyph,
+				 unsigned int glyph_stride,
+				 hb_position_t *first_x,
+				 unsigned int x_stride,
+				 hb_position_t *first_y,
+				 unsigned int y_stride,
+				 bool synthetic = true)
+
+  {
+    bool ret = klass->get.f.glyph_v_origins (this, user_data,
+					     count,
+					     first_glyph, glyph_stride,
+					     first_x, x_stride, first_y, y_stride,
+					     !klass->user_data ? nullptr : klass->user_data->glyph_v_origins);
+
+    if (synthetic && is_synthetic && ret)
+    {
+      hb_position_t x_shift = x_scale < 0 ? -x_strength : x_strength;
+      hb_position_t y_shift = y_scale < 0 ? -y_strength : y_strength;
+      for (unsigned i = 0; i < count; i++)
+      {
+	/* Slant */
+	if (slant_xy)
+	  *first_x += roundf (*first_y * slant_xy);
+
+	/* Embolden */
+	if (!embolden_in_place)
+	{
+	  *first_x += x_shift;
+	  *first_y += y_shift;
+	}
+      }
+      first_x = &StructAtOffsetUnaligned<hb_position_t> (first_x, x_stride);
+      first_y = &StructAtOffsetUnaligned<hb_position_t> (first_y, y_stride);
+    }
+
+    return ret;
   }
 
   hb_position_t get_glyph_h_kerning (hb_codepoint_t left_glyph,
@@ -486,7 +605,7 @@ struct hb_font_t
 					 extents,
 					 !klass->user_data ? nullptr : klass->user_data->glyph_extents);
     }
-    if (!is_synthetic () &&
+    if (!is_synthetic &&
 	klass->get.f.glyph_extents (this, user_data,
 				    glyph,
 				    extents,
@@ -508,7 +627,7 @@ struct hb_font_t
 #endif
 
 #ifndef HB_NO_DRAW
-    hb_extents_t draw_extents;
+    hb_extents_t<> draw_extents;
     if (draw_glyph_or_fail (glyph,
 			    hb_draw_extents_get_funcs (), &draw_extents))
     {
@@ -714,6 +833,28 @@ struct hb_font_t
       get_glyph_v_advances (count, first_glyph, glyph_stride, first_advance, advance_stride);
   }
 
+  void apply_offset (hb_position_t *x, hb_position_t *y,
+		     hb_position_t dx, hb_position_t dy,
+		     signed mult)
+  {
+    assert (mult == -1 || mult == +1);
+
+    *x += dx * mult;
+    *y += dy * mult;
+  }
+  void add_offset (hb_position_t *x, hb_position_t *y,
+		   hb_position_t dx, hb_position_t dy)
+  {
+    *x += dx;
+    *y += dy;
+  }
+  void subtract_offset (hb_position_t *x, hb_position_t *y,
+			hb_position_t dx, hb_position_t dy)
+  {
+    *x -= dx;
+    *y -= dy;
+  }
+
   void guess_v_origin_minus_h_origin (hb_codepoint_t glyph,
 				      hb_position_t *x, hb_position_t *y)
   {
@@ -724,6 +865,141 @@ struct hb_font_t
     *y = extents.ascender;
   }
 
+  void apply_glyph_h_origins_with_fallback (hb_buffer_t *buf, int mult)
+  {
+    bool has_ascender = false;
+    hb_position_t ascender = 0;
+
+    struct { hb_position_t x, y; } origins[32];
+
+    unsigned int offset = 0;
+    unsigned int count = buf->len;
+    while (offset < count)
+    {
+      unsigned n = hb_min (count - offset, ARRAY_LENGTH (origins));
+      if (!get_glyph_h_origins (n,
+				&buf->info[offset].codepoint, sizeof (hb_glyph_info_t),
+				&origins[0].x, sizeof (origins[0]),
+				&origins[0].y, sizeof (origins[0])))
+      {
+        if (get_glyph_v_origins (n,
+				  &buf->info[offset].codepoint, sizeof (hb_glyph_info_t),
+				  &origins[0].x, sizeof (origins[0]),
+				  &origins[0].y, sizeof (origins[0])))
+	{
+	  if (!has_ascender)
+	  {
+	    hb_font_extents_t extents;
+	    get_h_extents_with_fallback (&extents);
+	    ascender = extents.ascender;
+	    has_ascender = true;
+	  }
+
+	  /* We got the v_origins, adjust them to h_origins. */
+	  for (unsigned j = 0; j < n; j++)
+	  {
+	    hb_codepoint_t glyph = buf->info[offset + j].codepoint;
+	    origins[j].x -= get_glyph_h_advance (glyph) / 2;
+	    origins[j].y -= ascender;
+	  }
+	}
+	else
+	{
+	  for (unsigned j = 0; j < n; j++)
+	  {
+	    origins[j].x = 0;
+	    origins[j].y = 0;
+	  }
+	}
+      }
+
+      assert (mult == -1 || mult == +1);
+      if (mult == +1)
+        for (unsigned j = 0; j < n; j++)
+	{
+	  hb_glyph_position_t *pos = &buf->pos[offset + j];
+	  add_offset (&pos->x_offset, &pos->y_offset,
+		      origins[j].x, origins[j].y);
+	}
+      else /* mult == -1 */
+	for (unsigned j = 0; j < n; j++)
+	{
+	  hb_glyph_position_t *pos = &buf->pos[offset + j];
+	  subtract_offset (&pos->x_offset, &pos->y_offset,
+			   origins[j].x, origins[j].y);
+	}
+
+      offset += n;
+    }
+  }
+  void apply_glyph_v_origins_with_fallback (hb_buffer_t *buf, int mult)
+  {
+    bool has_ascender = false;
+    hb_position_t ascender = 0;
+
+    struct { hb_position_t x, y; } origins[32];
+
+    unsigned int offset = 0;
+    unsigned int count = buf->len;
+    while (offset < count)
+    {
+      unsigned n = hb_min (count - offset, ARRAY_LENGTH (origins));
+      if (!get_glyph_v_origins (n,
+				&buf->info[offset].codepoint, sizeof (hb_glyph_info_t),
+				&origins[0].x, sizeof (origins[0]),
+				&origins[0].y, sizeof (origins[0])))
+      {
+	if (get_glyph_h_origins (n,
+				 &buf->info[offset].codepoint, sizeof (hb_glyph_info_t),
+				 &origins[0].x, sizeof (origins[0]),
+				 &origins[0].y, sizeof (origins[0])))
+	{
+	  if (!has_ascender)
+	  {
+	    hb_font_extents_t extents;
+	    get_h_extents_with_fallback (&extents);
+	    ascender = extents.ascender;
+	    has_ascender = true;
+	  }
+
+	  /* We got the h_origins, adjust them to v_origins. */
+	  for (unsigned j = 0; j < n; j++)
+	  {
+	    hb_codepoint_t glyph = buf->info[offset + j].codepoint;
+	    origins[j].x += get_glyph_h_advance (glyph) / 2;
+	    origins[j].y += ascender;
+	  }
+	}
+	else
+	{
+	  for (unsigned j = 0; j < n; j++)
+	  {
+	    origins[j].x = 0;
+	    origins[j].y = 0;
+	  }
+	}
+      }
+
+      assert (mult == -1 || mult == +1);
+      if (mult == +1)
+        for (unsigned j = 0; j < n; j++)
+	{
+	  hb_glyph_position_t *pos = &buf->pos[offset + j];
+	  add_offset (&pos->x_offset, &pos->y_offset,
+		      origins[j].x, origins[j].y);
+	}
+      else /* mult == -1 */
+	for (unsigned j = 0; j < n; j++)
+	{
+	  hb_glyph_position_t *pos = &buf->pos[offset + j];
+	  subtract_offset (&pos->x_offset, &pos->y_offset,
+			   origins[j].x, origins[j].y);
+	}
+
+      offset += n;
+    }
+  }
+
   void get_glyph_h_origin_with_fallback (hb_codepoint_t glyph,
 					 hb_position_t *x, hb_position_t *y)
   {
@@ -732,7 +1008,7 @@ struct hb_font_t
     {
       hb_position_t dx, dy;
       guess_v_origin_minus_h_origin (glyph, &dx, &dy);
-      *x -= dx; *y -= dy;
+      subtract_offset (x, y, dx, dy);
     }
   }
   void get_glyph_v_origin_with_fallback (hb_codepoint_t glyph,
@@ -743,7 +1019,7 @@ struct hb_font_t
     {
       hb_position_t dx, dy;
       guess_v_origin_minus_h_origin (glyph, &dx, &dy);
-      *x += dx; *y += dy;
+      add_offset (x, y, dx, dy);
     }
   }
 
@@ -757,68 +1033,38 @@ struct hb_font_t
       get_glyph_v_origin_with_fallback (glyph, x, y);
   }
 
-  void add_glyph_h_origin (hb_codepoint_t glyph,
-			   hb_position_t *x, hb_position_t *y)
+  void add_glyph_h_origins (hb_buffer_t *buf)
   {
-    hb_position_t origin_x, origin_y;
-
-    get_glyph_h_origin_with_fallback (glyph, &origin_x, &origin_y);
-
-    *x += origin_x;
-    *y += origin_y;
+    apply_glyph_h_origins_with_fallback (buf, +1);
   }
-  void add_glyph_v_origin (hb_codepoint_t glyph,
-			   hb_position_t *x, hb_position_t *y)
+  void add_glyph_v_origins (hb_buffer_t *buf)
   {
-    hb_position_t origin_x, origin_y;
-
-    get_glyph_v_origin_with_fallback (glyph, &origin_x, &origin_y);
-
-    *x += origin_x;
-    *y += origin_y;
+    apply_glyph_v_origins_with_fallback (buf, +1);
   }
   void add_glyph_origin_for_direction (hb_codepoint_t glyph,
 				       hb_direction_t direction,
 				       hb_position_t *x, hb_position_t *y)
   {
     hb_position_t origin_x, origin_y;
-
     get_glyph_origin_for_direction (glyph, direction, &origin_x, &origin_y);
-
-    *x += origin_x;
-    *y += origin_y;
+    add_offset (x, y, origin_x, origin_y);
   }
 
-  void subtract_glyph_h_origin (hb_codepoint_t glyph,
-				hb_position_t *x, hb_position_t *y)
+  void subtract_glyph_h_origins (hb_buffer_t *buf)
   {
-    hb_position_t origin_x, origin_y;
-
-    get_glyph_h_origin_with_fallback (glyph, &origin_x, &origin_y);
-
-    *x -= origin_x;
-    *y -= origin_y;
+    apply_glyph_h_origins_with_fallback (buf, -1);
   }
-  void subtract_glyph_v_origin (hb_codepoint_t glyph,
-				hb_position_t *x, hb_position_t *y)
+  void subtract_glyph_v_origins (hb_buffer_t *buf)
   {
-    hb_position_t origin_x, origin_y;
-
-    get_glyph_v_origin_with_fallback (glyph, &origin_x, &origin_y);
-
-    *x -= origin_x;
-    *y -= origin_y;
+    apply_glyph_v_origins_with_fallback (buf, -1);
   }
   void subtract_glyph_origin_for_direction (hb_codepoint_t glyph,
 					    hb_direction_t direction,
 					    hb_position_t *x, hb_position_t *y)
   {
     hb_position_t origin_x, origin_y;
-
     get_glyph_origin_for_direction (glyph, direction, &origin_x, &origin_y);
-
-    *x -= origin_x;
-    *y -= origin_y;
+    subtract_offset (x, y, origin_x, origin_y);
   }
 
   void get_glyph_kerning_for_direction (hb_codepoint_t first_glyph, hb_codepoint_t second_glyph,
@@ -900,11 +1146,6 @@ struct hb_font_t
     return false;
   }
 
-  bool is_synthetic () const
-  {
-    return x_embolden || y_embolden || slant;
-  }
-
   void changed ()
   {
     float upem = face->get_upem ();
@@ -916,6 +1157,8 @@ struct hb_font_t
     bool y_neg = y_scale < 0;
     y_mult = (y_neg ? -((int64_t) -y_scale << 16) : ((int64_t) y_scale << 16)) / upem;
 
+    is_synthetic =  x_embolden || y_embolden || slant;
+
     x_strength = roundf (abs (x_scale) * x_embolden);
     y_strength = roundf (abs (y_scale) * y_embolden);
 

+ 9 - 21
thirdparty/harfbuzz/src/hb-ft-colr.hh

@@ -90,7 +90,7 @@ struct hb_ft_paint_context_t
     funcs (paint_funcs), data (paint_data),
     palette (palette), palette_index (palette_index), foreground (foreground)
   {
-    if (font->is_synthetic ())
+    if (font->is_synthetic)
     {
       font = hb_font_create_sub_font (font);
       hb_font_set_synthetic_bold (font, 0, 0, true);
@@ -412,9 +412,9 @@ _hb_ft_paint (hb_ft_paint_context_t *c,
       float dx = paint.u.translate.dx / 65536.f;
       float dy = paint.u.translate.dy / 65536.f;
 
-      bool p1 = c->funcs->push_translate (c->data, dx, dy);
+      c->funcs->push_translate (c->data, dx, dy);
       c->recurse (paint.u.translate.paint);
-      if (p1) c->funcs->pop_transform (c->data);
+      c->funcs->pop_transform (c->data);
     }
     break;
     case FT_COLR_PAINTFORMAT_SCALE:
@@ -424,13 +424,9 @@ _hb_ft_paint (hb_ft_paint_context_t *c,
       float sx = paint.u.scale.scale_x / 65536.f;
       float sy = paint.u.scale.scale_y / 65536.f;
 
-      bool p1 = c->funcs->push_translate (c->data, +dx, +dy);
-      bool p2 = c->funcs->push_scale (c->data, sx, sy);
-      bool p3 = c->funcs->push_translate (c->data, -dx, -dy);
+      c->funcs->push_scale_around_center (c->data, sx, sy, dx, dy);
       c->recurse (paint.u.scale.paint);
-      if (p3) c->funcs->pop_transform (c->data);
-      if (p2) c->funcs->pop_transform (c->data);
-      if (p1) c->funcs->pop_transform (c->data);
+      c->funcs->pop_transform (c->data);
     }
     break;
     case FT_COLR_PAINTFORMAT_ROTATE:
@@ -439,13 +435,9 @@ _hb_ft_paint (hb_ft_paint_context_t *c,
       float dy = paint.u.rotate.center_y / 65536.f;
       float a = paint.u.rotate.angle / 65536.f;
 
-      bool p1 = c->funcs->push_translate (c->data, +dx, +dy);
-      bool p2 = c->funcs->push_rotate (c->data, a);
-      bool p3 = c->funcs->push_translate (c->data, -dx, -dy);
+      c->funcs->push_rotate_around_center (c->data, a, dx, dy);
       c->recurse (paint.u.rotate.paint);
-      if (p3) c->funcs->pop_transform (c->data);
-      if (p2) c->funcs->pop_transform (c->data);
-      if (p1) c->funcs->pop_transform (c->data);
+      c->funcs->pop_transform (c->data);
     }
     break;
     case FT_COLR_PAINTFORMAT_SKEW:
@@ -455,13 +447,9 @@ _hb_ft_paint (hb_ft_paint_context_t *c,
       float sx = paint.u.skew.x_skew_angle / 65536.f;
       float sy = paint.u.skew.y_skew_angle / 65536.f;
 
-      bool p1 = c->funcs->push_translate (c->data, +dx, +dy);
-      bool p2 = c->funcs->push_skew (c->data, sx, sy);
-      bool p3 = c->funcs->push_translate (c->data, -dx, -dy);
+      c->funcs->push_skew_around_center (c->data, sx, sy, dx, dy);
       c->recurse (paint.u.skew.paint);
-      if (p3) c->funcs->pop_transform (c->data);
-      if (p2) c->funcs->pop_transform (c->data);
-      if (p1) c->funcs->pop_transform (c->data);
+      c->funcs->pop_transform (c->data);
     }
     break;
     case FT_COLR_PAINTFORMAT_COMPOSITE:

+ 25 - 5
thirdparty/harfbuzz/src/hb-ft.cc

@@ -143,6 +143,9 @@ _hb_ft_font_destroy (void *data)
 /* hb_font changed, update FT_Face. */
 static void _hb_ft_hb_font_changed (hb_font_t *font, FT_Face ft_face)
 {
+  if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy))
+    return;
+
   hb_ft_font_t *ft_font = (hb_ft_font_t *) font->user_data;
 
   float x_mult = 1.f, y_mult = 1.f;
@@ -184,12 +187,14 @@ static void _hb_ft_hb_font_changed (hb_font_t *font, FT_Face ft_face)
     FT_Set_Transform (ft_face, &matrix, nullptr);
     ft_font->transform = true;
   }
+  else
+    FT_Set_Transform (ft_face, nullptr, nullptr);
 
 #if defined(HAVE_FT_GET_VAR_BLEND_COORDINATES) && !defined(HB_NO_VAR)
-  unsigned int num_coords;
-  const float *coords = hb_font_get_var_coords_design (font, &num_coords);
-  if (num_coords)
+  if (font->has_nonzero_coords)
   {
+    unsigned int num_coords;
+    const float *coords = hb_font_get_var_coords_design (font, &num_coords);
     FT_Fixed *ft_coords = (FT_Fixed *) hb_calloc (num_coords, sizeof (FT_Fixed));
     if (ft_coords)
     {
@@ -199,6 +204,12 @@ static void _hb_ft_hb_font_changed (hb_font_t *font, FT_Face ft_face)
       hb_free (ft_coords);
     }
   }
+  else if (font->num_coords)
+  {
+    // Some old versions of FreeType crash if we
+    // call this function on non-variable fonts.
+    FT_Set_Var_Design_Coordinates (ft_face, 0, nullptr);
+  }
 #endif
 }
 
@@ -1093,6 +1104,10 @@ _hb_ft_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data
   FT_ULong  length = 0;
   FT_Error error;
 
+  /* In new FreeType, a tag value of 1 loads the SFNT table directory. Reject it. */
+  if (tag == 1)
+    return nullptr;
+
   /* Note: FreeType like HarfBuzz uses the NONE tag for fetching the entire blob */
 
   error = FT_Load_Sfnt_Table (ft_face, tag, 0, nullptr, &length);
@@ -1366,7 +1381,7 @@ hb_ft_font_changed (hb_font_t *font)
 
 	for (unsigned int i = 0; i < mm_var->num_axis; ++i)
 	 {
-	  coords[i] = ft_coords[i] >>= 2;
+	  coords[i] = (ft_coords[i] + 2) >> 2;
 	  nonzero = nonzero || coords[i];
 	 }
 
@@ -1717,7 +1732,12 @@ hb_ft_font_set_funcs (hb_font_t *font)
   ft_face->generic.finalizer = _release_blob;
 
   // And the FT_Library to the blob
-  hb_blob_set_user_data (blob, &ft_library_key, ft_library, destroy_ft_library, true);
+  if (unlikely (!hb_blob_set_user_data (blob, &ft_library_key, ft_library, destroy_ft_library, true)))
+  {
+    DEBUG_MSG (FT, font, "hb_blob_set_user_data() failed");
+    FT_Done_Face (ft_face);
+    return;
+  }
 
   _hb_ft_font_set_funcs (font, ft_face, true);
   hb_ft_font_set_load_flags (font, FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING);

+ 158 - 89
thirdparty/harfbuzz/src/hb-geometry.hh

@@ -26,7 +26,10 @@
 
 #include "hb.hh"
 
+#include "hb-algs.hh"
 
+
+template <typename Float = float>
 struct hb_extents_t
 {
   hb_extents_t () {}
@@ -35,7 +38,7 @@ struct hb_extents_t
 		ymin (hb_min (extents.y_bearing, extents.y_bearing + extents.height)),
 		xmax (hb_max (extents.x_bearing, extents.x_bearing + extents.width)),
 		ymax (hb_max (extents.y_bearing, extents.y_bearing + extents.height)) {}
-  hb_extents_t (float xmin, float ymin, float xmax, float ymax) :
+  hb_extents_t (Float xmin, Float ymin, Float xmax, Float ymax) :
     xmin (xmin), ymin (ymin), xmax (xmax), ymax (ymax) {}
 
   bool is_empty () const { return xmin >= xmax || ymin >= ymax; }
@@ -69,7 +72,7 @@ struct hb_extents_t
   }
 
   void
-  add_point (float x, float y)
+  add_point (Float x, Float y)
   {
     if (unlikely (is_void ()))
     {
@@ -97,62 +100,69 @@ struct hb_extents_t
 			       yneg ? y1 - y0 : y0 - y1};
   }
 
-  float xmin = 0.f;
-  float ymin = 0.f;
-  float xmax = -1.f;
-  float ymax = -1.f;
+  Float xmin = 0;
+  Float ymin = 0;
+  Float xmax = -1;
+  Float ymax = -1;
 };
 
+template <typename Float = float>
 struct hb_transform_t
 {
   hb_transform_t () {}
-  hb_transform_t (float xx, float yx,
-		  float xy, float yy,
-		  float x0, float y0) :
+  hb_transform_t (Float xx, Float yx,
+		  Float xy, Float yy,
+		  Float x0, Float 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;
+    return xx == 1 && yx == 0 &&
+	   xy == 0 && yy == 1 &&
+	   x0 == 0 && y0 == 0;
   }
-
-  void multiply (const hb_transform_t &o)
+  bool is_translation () const
   {
-    /* Copied from cairo, with "o" being "a" there and "this" being "b" there. */
-    hb_transform_t r;
-
-    r.xx = o.xx * xx + o.yx * xy;
-    r.yx = o.xx * yx + o.yx * yy;
-
-    r.xy = o.xy * xx + o.yy * xy;
-    r.yy = o.xy * yx + o.yy * yy;
-
-    r.x0 = o.x0 * xx + o.y0 * xy + x0;
-    r.y0 = o.x0 * yx + o.y0 * yy + y0;
+    return xx == 1 && yx == 0 &&
+	   xy == 0 && yy == 1;
+  }
 
-    *this = r;
+  void multiply (const hb_transform_t &o, bool before=false)
+  {
+    // Copied from cairo-matrix.c
+    const hb_transform_t &a = before ? o : *this;
+    const hb_transform_t &b = before ? *this : o;
+    *this = {
+      a.xx * b.xx + a.xy * b.yx,
+      a.yx * b.xx + a.yy * b.yx,
+      a.xx * b.xy + a.xy * b.yy,
+      a.yx * b.xy + a.yy * b.yy,
+      a.xx * b.x0 + a.xy * b.y0 + a.x0,
+      a.yx * b.x0 + a.yy * b.y0 + a.y0
+    };
   }
 
-  void transform_distance (float &dx, float &dy) const
+  HB_ALWAYS_INLINE
+  void transform_distance (Float &dx, Float &dy) const
   {
-    float new_x = xx * dx + xy * dy;
-    float new_y = yx * dx + yy * dy;
+    Float new_x = xx * dx + xy * dy;
+    Float new_y = yx * dx + yy * dy;
     dx = new_x;
     dy = new_y;
   }
 
-  void transform_point (float &x, float &y) const
+  HB_ALWAYS_INLINE
+  void transform_point (Float &x, Float &y) const
   {
-    transform_distance (x, y);
-    x += x0;
-    y += y0;
+    Float new_x = x0 + xx * x + xy * y;
+    Float new_y = y0 + yx * x + yy * y;
+    x = new_x;
+    y = new_y;
   }
 
-  void transform_extents (hb_extents_t &extents) const
+  void transform_extents (hb_extents_t<Float> &extents) const
   {
-    float quad_x[4], quad_y[4];
+    Float quad_x[4], quad_y[4];
 
     quad_x[0] = extents.xmin;
     quad_y[0] = extents.ymin;
@@ -163,7 +173,7 @@ struct hb_transform_t
     quad_x[3] = extents.xmax;
     quad_y[3] = extents.ymax;
 
-    extents = hb_extents_t {};
+    extents = hb_extents_t<Float> {};
     for (unsigned i = 0; i < 4; i++)
     {
       transform_point (quad_x[i], quad_y[i]);
@@ -171,20 +181,36 @@ struct hb_transform_t
     }
   }
 
-  void transform (const hb_transform_t &o) { multiply (o); }
+  void transform (const hb_transform_t &o, bool before=false) { multiply (o, before); }
 
-  void translate (float x, float y)
+  static hb_transform_t translation (Float x, Float y)
   {
-    if (x == 0.f && y == 0.f)
-      return;
+    return {1, 0, 0, 1, x, y};
+  }
+  void translate (Float x, Float y, bool before=false)
+  {
+    if (before)
+    {
+      x0 += x;
+      y0 += y;
+    }
+    else
+    {
+      if (x == 0 && y == 0)
+	return;
 
-    x0 += xx * x + xy * y;
-    y0 += yx * x + yy * y;
+      x0 += xx * x + xy * y;
+      y0 += yx * x + yy * y;
+    }
   }
 
-  void scale (float scaleX, float scaleY)
+  static hb_transform_t scaling (Float scaleX, Float scaleY)
+  {
+    return {scaleX, 0, 0, scaleY, 0, 0};
+  }
+  void scale (Float scaleX, Float scaleY)
   {
-    if (scaleX == 1.f && scaleY == 1.f)
+    if (scaleX == 1 && scaleY == 1)
       return;
 
     xx *= scaleX;
@@ -192,52 +218,94 @@ struct hb_transform_t
     xy *= scaleY;
     yy *= scaleY;
   }
-
-  void rotate (float rotation)
+  static hb_transform_t scaling_around_center (Float scaleX, Float scaleY, Float center_x, Float center_y)
   {
-    if (rotation == 0.f)
+    return {scaleX, 0, 0, scaleY,
+	    center_x ? (1 - scaleX) * center_x : 0,
+	    center_y ? (1 - scaleY) * center_y : 0};
+  }
+  void scale_around_center (Float scaleX, Float scaleY, Float center_x, Float center_y)
+  {
+    if (scaleX == 1 && scaleY == 1)
       return;
 
+    transform (scaling_around_center (scaleX, scaleY, center_x, center_y));
+  }
+
+  static hb_transform_t rotation (Float radians)
+  {
     // https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L240
-    rotation = rotation * HB_PI;
-    float c;
-    float s;
-#ifdef HAVE_SINCOSF
-    sincosf (rotation, &s, &c);
-#else
-    c = cosf (rotation);
-    s = sinf (rotation);
-#endif
-    auto other = hb_transform_t{c, s, -s, c, 0.f, 0.f};
-    transform (other);
+    Float c;
+    Float s;
+    hb_sincos (radians, s, c);
+    return {c, s, -s, c, 0, 0};
+  }
+  void rotate (Float radians, bool before=false)
+  {
+    if (radians == 0)
+      return;
+
+    transform (rotation (radians), before);
+  }
+
+  static hb_transform_t rotation_around_center (Float radians, Float center_x, Float center_y)
+  {
+    Float s, c;
+    hb_sincos (radians, s, c);
+    return {
+      c, s, -s, c,
+      (1 - c) * center_x + s * center_y,
+      -s * center_x +  (1 - c) * center_y
+    };
+  }
+  void rotate_around_center (Float radians, Float center_x, Float center_y, bool before=false)
+  {
+    if (radians == 0)
+      return;
+
+    transform (rotation_around_center (radians, center_x, center_y), before);
   }
 
-  void skew (float skewX, float skewY)
+  static hb_transform_t skewing (Float skewX, Float skewY)
+  {
+    return {1, skewY ? tanf (skewY) : 0, skewX ? tanf (skewX) : 0, 1, 0, 0};
+  }
+  void skew (Float skewX, Float skewY)
   {
-    if (skewX == 0.f && skewY == 0.f)
+    if (skewX == 0 && skewY == 0)
       return;
 
-    // https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L255
-    skewX = skewX * HB_PI;
-    skewY = skewY * HB_PI;
-    auto other = hb_transform_t{1.f,
-				skewY ? tanf (skewY) : 0.f,
-				skewX ? tanf (skewX) : 0.f,
-				1.f,
-				0.f, 0.f};
-    transform (other);
+    transform (skewing (skewX, skewY));
+  }
+  static hb_transform_t skewing_around_center (Float skewX, Float skewY, Float center_x, Float center_y)
+  {
+    skewX = skewX ? tanf (skewX) : 0;
+    skewY = skewY ? tanf (skewY) : 0;
+    return {
+	    1, skewY, skewX, 1,
+	    center_y ? -skewX * center_y : 0,
+	    center_x ? -skewY * center_x : 0
+    };
+  }
+  void skew_around_center (Float skewX, Float skewY, Float center_x, Float center_y)
+  {
+    if (skewX == 0 && skewY == 0)
+	    return;
+
+    transform (skewing_around_center (skewX, skewY, center_x, center_y));
   }
 
-  float xx = 1.f;
-  float yx = 0.f;
-  float xy = 0.f;
-  float yy = 1.f;
-  float x0 = 0.f;
-  float y0 = 0.f;
+  Float xx = 1;
+  Float yx = 0;
+  Float xy = 0;
+  Float yy = 1;
+  Float x0 = 0;
+  Float y0 = 0;
 };
 
-#define HB_TRANSFORM_IDENTITY hb_transform_t{1.f, 0.f, 0.f, 1.f, 0.f, 0.f}
+#define HB_TRANSFORM_IDENTITY {1, 0, 0, 1, 0, 0}
 
+template <typename Float = float>
 struct hb_bounds_t
 {
   enum status_t {
@@ -247,7 +315,7 @@ struct hb_bounds_t
   };
 
   hb_bounds_t (status_t status = UNBOUNDED) : status (status) {}
-  hb_bounds_t (const hb_extents_t &extents) :
+  hb_bounds_t (const hb_extents_t<Float> &extents) :
     status (extents.is_empty () ? EMPTY : BOUNDED), extents (extents) {}
 
   void union_ (const hb_bounds_t &o)
@@ -281,20 +349,21 @@ struct hb_bounds_t
   }
 
   status_t status;
-  hb_extents_t extents;
+  hb_extents_t<Float> extents;
 };
 
+template <typename Float = float>
 struct hb_transform_decomposed_t
 {
-  float translateX = 0;
-  float translateY = 0;
-  float rotation = 0;  // in degrees, counter-clockwise
-  float scaleX = 1;
-  float scaleY = 1;
-  float skewX = 0;  // in degrees, counter-clockwise
-  float skewY = 0;  // in degrees, counter-clockwise
-  float tCenterX = 0;
-  float tCenterY = 0;
+  Float translateX = 0;
+  Float translateY = 0;
+  Float rotation = 0;  // in radians, counter-clockwise
+  Float scaleX = 1;
+  Float scaleY = 1;
+  Float skewX = 0;  // in radians, counter-clockwise
+  Float skewY = 0;  // in radians, counter-clockwise
+  Float tCenterX = 0;
+  Float tCenterY = 0;
 
   operator bool () const
   {
@@ -305,9 +374,9 @@ struct hb_transform_decomposed_t
 	   tCenterX || tCenterY;
   }
 
-  hb_transform_t to_transform () const
+  hb_transform_t<Float> to_transform () const
   {
-    hb_transform_t t;
+    hb_transform_t<Float> t;
     t.translate (translateX + tCenterX, translateY + tCenterY);
     t.rotate (rotation);
     t.scale (scaleX, scaleY);

+ 0 - 8
thirdparty/harfbuzz/src/hb-glib.cc

@@ -127,11 +127,7 @@ hb_glib_unicode_compose (hb_unicode_funcs_t *ufuncs HB_UNUSED,
 			 hb_codepoint_t     *ab,
 			 void               *user_data HB_UNUSED)
 {
-#if GLIB_CHECK_VERSION(2,29,12)
   return g_unichar_compose (a, b, ab);
-#else
-  return false;
-#endif
 }
 
 static hb_bool_t
@@ -141,11 +137,7 @@ hb_glib_unicode_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED,
 			   hb_codepoint_t     *b,
 			   void               *user_data HB_UNUSED)
 {
-#if GLIB_CHECK_VERSION(2,29,12)
   return g_unichar_decompose (ab, a, b);
-#else
-  return false;
-#endif
 }
 
 

+ 0 - 6
thirdparty/harfbuzz/src/hb-gobject-structs.cc

@@ -51,12 +51,6 @@
 
 /* g++ didn't like older gtype.h gcc-only code path. */
 #include <glib.h>
-#if !GLIB_CHECK_VERSION(2,29,16)
-#undef __GNUC__
-#undef __GNUC_MINOR__
-#define __GNUC__ 2
-#define __GNUC_MINOR__ 6
-#endif
 
 #include "hb-gobject.h"
 

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

@@ -66,13 +66,15 @@ static inline Type& StructAtOffsetUnaligned(void *P, unsigned int offset)
 }
 
 /* StructAfter<T>(X) returns the struct T& that is placed after X.
- * Works with X of variable size also.  X must implement get_size() */
-template<typename Type, typename TObject>
-static inline const Type& StructAfter(const TObject &X)
-{ return StructAtOffset<Type>(&X, X.get_size()); }
-template<typename Type, typename TObject>
-static inline Type& StructAfter(TObject &X)
-{ return StructAtOffset<Type>(&X, X.get_size()); }
+ * Works with X of variable size also.  X must implement get_size().
+ * Any extra arguments are forwarded to get_size, so for example
+ * it can work with UnsizedArrayOf<> as well. */
+template <typename Type, typename TObject, typename ...Ts>
+static inline const Type& StructAfter(const TObject &X, Ts... args)
+{ return StructAtOffset<Type>(&X, X.get_size(args...)); }
+template <typename Type, typename TObject, typename ...Ts>
+static inline Type& StructAfter(TObject &X, Ts... args)
+{ return StructAtOffset<Type>(&X, X.get_size(args...)); }
 
 
 /*
@@ -132,7 +134,6 @@ static inline Type& StructAfter(TObject &X)
   DEFINE_SIZE_ARRAY(size, array)
 
 
-
 /*
  * Lazy loaders.
  *

+ 72 - 30
thirdparty/harfbuzz/src/hb-open-type.hh

@@ -54,35 +54,40 @@ namespace OT {
  */
 
 /* Integer types in big-endian order and no alignment requirement */
-template <typename Type,
+template <bool BE,
+	  typename Type,
 	  unsigned int Size = sizeof (Type)>
-struct IntType
+struct NumType
 {
   typedef Type type;
-
-  IntType () = default;
-  explicit constexpr IntType (Type V) : v {V} {}
-  IntType& operator = (Type i) { v = i; return *this; }
   /* For reason we define cast out operator for signed/unsigned, instead of Type, see:
    * https://github.com/harfbuzz/harfbuzz/pull/2875/commits/09836013995cab2b9f07577a179ad7b024130467 */
-  operator typename std::conditional<std::is_signed<Type>::value, signed, unsigned>::type () const { return v; }
+  typedef typename std::conditional<std::is_integral<Type>::value,
+				     typename std::conditional<std::is_signed<Type>::value, signed, unsigned>::type,
+				     Type>::type WideType;
+
+  NumType () = default;
+  explicit constexpr NumType (Type V) : v {V} {}
+  NumType& operator = (Type V) { v = V; return *this; }
+
+  operator WideType () const { return v; }
 
-  bool operator == (const IntType &o) const { return (Type) v == (Type) o.v; }
-  bool operator != (const IntType &o) const { return !(*this == o); }
+  bool operator == (const NumType &o) const { return (Type) v == (Type) o.v; }
+  bool operator != (const NumType &o) const { return !(*this == o); }
 
-  IntType& operator += (unsigned count) { *this = *this + count; return *this; }
-  IntType& operator -= (unsigned count) { *this = *this - count; return *this; }
-  IntType& operator ++ () { *this += 1; return *this; }
-  IntType& operator -- () { *this -= 1; return *this; }
-  IntType operator ++ (int) { IntType c (*this); ++*this; return c; }
-  IntType operator -- (int) { IntType c (*this); --*this; return c; }
+  NumType& operator += (WideType count) { *this = *this + count; return *this; }
+  NumType& operator -= (WideType count) { *this = *this - count; return *this; }
+  NumType& operator ++ () { *this += 1; return *this; }
+  NumType& operator -- () { *this -= 1; return *this; }
+  NumType operator ++ (int) { NumType c (*this); ++*this; return c; }
+  NumType operator -- (int) { NumType c (*this); --*this; return c; }
 
-  HB_INTERNAL static int cmp (const IntType *a, const IntType *b)
+  HB_INTERNAL static int cmp (const NumType *a, const NumType *b)
   { return b->cmp (*a); }
   HB_INTERNAL static int cmp (const void *a, const void *b)
   {
-    IntType *pa = (IntType *) a;
-    IntType *pb = (IntType *) b;
+    NumType *pa = (NumType *) a;
+    NumType *pb = (NumType *) b;
 
     return pb->cmp (*pa);
   }
@@ -99,20 +104,32 @@ struct IntType
     return_trace (c->check_struct (this));
   }
   protected:
-  BEInt<Type, Size> v;
+  typename std::conditional<std::is_integral<Type>::value,
+			    HBInt<BE, Type, Size>,
+			    HBFloat<BE, Type, Size>>::type v;
   public:
   DEFINE_SIZE_STATIC (Size);
 };
 
-typedef IntType<uint8_t>  HBUINT8;	/* 8-bit unsigned integer. */
-typedef IntType<int8_t>   HBINT8;	/* 8-bit signed integer. */
-typedef IntType<uint16_t> HBUINT16;	/* 16-bit unsigned integer. */
-typedef IntType<int16_t>  HBINT16;	/* 16-bit signed integer. */
-typedef IntType<uint32_t> HBUINT32;	/* 32-bit unsigned integer. */
-typedef IntType<int32_t>  HBINT32;	/* 32-bit signed integer. */
+typedef NumType<true, uint8_t>  HBUINT8;	/* 8-bit big-endian unsigned integer. */
+typedef NumType<true, int8_t>   HBINT8;		/* 8-bit big-endian signed integer. */
+typedef NumType<true, uint16_t> HBUINT16;	/* 16-bit big-endian unsigned integer. */
+typedef NumType<true, int16_t>  HBINT16;	/* 16-bit big-endian signed integer. */
+typedef NumType<true, uint32_t> HBUINT32;	/* 32-bit big-endian unsigned integer. */
+typedef NumType<true, int32_t>  HBINT32;	/* 32-bit big-endian signed integer. */
 /* Note: we cannot defined a signed HBINT24 because there's no corresponding C type.
  * Works for unsigned, but not signed, since we rely on compiler for sign-extension. */
-typedef IntType<uint32_t, 3> HBUINT24;	/* 24-bit unsigned integer. */
+typedef NumType<true, uint32_t, 3> HBUINT24;	/* 24-bit big-endian unsigned integer. */
+
+typedef NumType<false, uint16_t> HBUINT16LE;	/* 16-bit little-endian unsigned integer. */
+typedef NumType<false, int16_t>  HBINT16LE;	/* 16-bit little-endian signed integer. */
+typedef NumType<false, uint32_t> HBUINT32LE;	/* 32-bit little-endian unsigned integer. */
+typedef NumType<false, int32_t>  HBINT32LE;	/* 32-bit little-endian signed integer. */
+
+typedef NumType<true,  float>  HBFLOAT32BE;	/* 32-bit little-endian floating point number. */
+typedef NumType<true,  double> HBFLOAT64BE;	/* 64-bit little-endian floating point number. */
+typedef NumType<false, float>  HBFLOAT32LE;	/* 32-bit little-endian floating point number. */
+typedef NumType<false, double> HBFLOAT64LE;	/* 64-bit little-endian floating point number. */
 
 /* 15-bit unsigned number; top bit used for extension. */
 struct HBUINT15 : HBUINT16
@@ -218,7 +235,7 @@ typedef HBUINT16 UFWORD;
 template <typename Type, unsigned fraction_bits>
 struct HBFixed : Type
 {
-  static constexpr float shift = (float) (1 << fraction_bits);
+  static constexpr float mult = 1.f / (1 << fraction_bits);
   static_assert (Type::static_size * 8 > fraction_bits, "");
 
   operator signed () const = delete;
@@ -226,8 +243,8 @@ struct HBFixed : Type
   explicit operator float () const { return to_float (); }
   typename Type::type to_int () const { return Type::v; }
   void set_int (typename Type::type i ) { Type::v = i; }
-  float to_float (float offset = 0) const  { return ((int32_t) Type::v + offset) / shift; }
-  void set_float (float f) { Type::v = roundf (f * shift); }
+  float to_float (float offset = 0) const  { return ((int32_t) Type::v + offset) * mult; }
+  void set_float (float f) { Type::v = roundf (f / mult); }
   public:
   DEFINE_SIZE_STATIC (Type::static_size);
 };
@@ -1707,7 +1724,8 @@ struct TupleValues
   static bool decompile (const HBUINT8 *&p /* IN/OUT */,
 			 hb_vector_t<T> &values /* IN/OUT */,
 			 const HBUINT8 *end,
-			 bool consume_all = false)
+			 bool consume_all = false,
+			 unsigned start = 0)
   {
     unsigned i = 0;
     unsigned count = consume_all ? UINT_MAX : values.length;
@@ -1725,6 +1743,10 @@ struct TupleValues
       }
       unsigned stop = i + run_count;
       if (unlikely (stop > count)) return false;
+
+      unsigned skip = i < start ? hb_min (start - i, run_count) : 0;
+      i += skip;
+
       if ((control & VALUES_SIZE_MASK) == VALUES_ARE_ZEROS)
       {
         for (; i < stop; i++)
@@ -1733,6 +1755,7 @@ struct TupleValues
       else if ((control & VALUES_SIZE_MASK) ==  VALUES_ARE_WORDS)
       {
         if (unlikely (p + run_count * HBINT16::static_size > end)) return false;
+	p += skip * HBINT16::static_size;
 #ifndef HB_OPTIMIZE_SIZE
         for (; i + 3 < stop; i += 4)
 	{
@@ -1755,6 +1778,7 @@ struct TupleValues
       else if ((control & VALUES_SIZE_MASK) ==  VALUES_ARE_LONGS)
       {
         if (unlikely (p + run_count * HBINT32::static_size > end)) return false;
+	p += skip * HBINT32::static_size;
         for (; i < stop; i++)
         {
           values.arrayZ[i] = * (const HBINT32 *) p;
@@ -1764,6 +1788,7 @@ struct TupleValues
       else if ((control & VALUES_SIZE_MASK) ==  VALUES_ARE_BYTES)
       {
         if (unlikely (p + run_count > end)) return false;
+	p += skip * HBINT8::static_size;
 #ifndef HB_OPTIMIZE_SIZE
 	for (; i + 3 < stop; i += 4)
 	{
@@ -2038,6 +2063,23 @@ struct TupleList : CFF2Index
 };
 
 
+// Alignment
+
+template <unsigned int alignment>
+struct Align
+{
+  unsigned get_size (const void *base) const
+  {
+    unsigned offset = (const char *) this - (const char *) base;
+    return (alignment - offset) & (alignment - 1);
+  }
+
+  public:
+  DEFINE_SIZE_MIN (0);
+};
+
+
+
 } /* namespace OT */
 
 

+ 5 - 1
thirdparty/harfbuzz/src/hb-ot-cff2-table.cc

@@ -202,7 +202,11 @@ struct cff2_cs_opset_path_t : cff2_cs_opset_t<cff2_cs_opset_path_t, cff2_path_pa
 
 bool OT::cff2::accelerator_t::get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const
 {
-  return get_path_at (font, glyph, draw_session, hb_array (font->coords, font->num_coords));
+  return get_path_at (font,
+		      glyph,
+		      draw_session,
+		      hb_array (font->coords,
+				font->has_nonzero_coords ? font->num_coords : 0));
 }
 
 bool OT::cff2::accelerator_t::get_path_at (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session, hb_array_t<const int> coords) const

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

@@ -501,10 +501,6 @@ struct CmapSubtableFormat4
     this->length = c->length () - table_initpos;
     if ((long long) this->length != (long long) c->length () - table_initpos)
     {
-      // Length overflowed. Discard the current object before setting the error condition, otherwise
-      // discard is a noop which prevents the higher level code from reverting the serializer to the
-      // pre-error state in cmap4 overflow handling code.
-      c->pop_discard ();
       c->err (HB_SERIALIZE_ERROR_INT_OVERFLOW);
       return;
     }
@@ -1646,7 +1642,7 @@ struct EncodingRecord
       CmapSubtable *cmapsubtable = c->push<CmapSubtable> ();
       unsigned origin_length = c->length ();
       cmapsubtable->serialize (c, it, format, plan, &(base+subtable));
-      if (c->length () - origin_length > 0) *objidx = c->pop_pack ();
+      if (c->length () - origin_length > 0 && !c->in_error()) *objidx = c->pop_pack ();
       else c->pop_discard ();
     }
 

+ 477 - 79
thirdparty/harfbuzz/src/hb-ot-font.cc

@@ -37,6 +37,7 @@
 
 #include "hb-ot-cmap-table.hh"
 #include "hb-ot-glyf-table.hh"
+#include "hb-ot-var-gvar-table.hh"
 #include "hb-ot-cff2-table.hh"
 #include "hb-ot-cff1-table.hh"
 #include "hb-ot-hmtx-table.hh"
@@ -64,18 +65,22 @@
 using hb_ot_font_advance_cache_t = hb_cache_t<24, 16>;
 static_assert (sizeof (hb_ot_font_advance_cache_t) == 1024, "");
 
+using hb_ot_font_origin_cache_t = hb_cache_t<20, 20>;
+static_assert (sizeof (hb_ot_font_origin_cache_t) == 1024, "");
+
 struct hb_ot_font_t
 {
   const hb_ot_face_t *ot_face;
 
-  /* h_advance caching */
+  mutable hb_atomic_t<int> cached_serial;
   mutable hb_atomic_t<int> cached_coords_serial;
-  struct advance_cache_t
+
+  struct direction_cache_t
   {
     mutable hb_atomic_t<hb_ot_font_advance_cache_t *> advance_cache;
-    mutable hb_atomic_t<OT::ItemVariationStore::cache_t *> varStore_cache;
+    mutable hb_atomic_t<OT::hb_scalar_cache_t *> varStore_cache;
 
-    ~advance_cache_t ()
+    ~direction_cache_t ()
     {
       clear ();
     }
@@ -116,7 +121,7 @@ struct hb_ot_font_t
         goto retry;
     }
 
-    OT::ItemVariationStore::cache_t *acquire_varStore_cache (const OT::ItemVariationStore &varStore) const
+    OT::hb_scalar_cache_t *acquire_varStore_cache (const OT::ItemVariationStore &varStore) const
     {
     retry:
       auto *cache = varStore_cache.get_acquire ();
@@ -127,7 +132,7 @@ struct hb_ot_font_t
       else
 	goto retry;
     }
-    void release_varStore_cache (OT::ItemVariationStore::cache_t *cache) const
+    void release_varStore_cache (OT::hb_scalar_cache_t *cache) const
     {
       if (!cache)
 	return;
@@ -154,17 +159,157 @@ struct hb_ot_font_t
 
   } h, v;
 
+  struct origin_cache_t
+  {
+    mutable hb_atomic_t<hb_ot_font_origin_cache_t *> origin_cache;
+    mutable hb_atomic_t<OT::hb_scalar_cache_t *> varStore_cache;
+
+    ~origin_cache_t ()
+    {
+      clear ();
+    }
+
+    hb_ot_font_origin_cache_t *acquire_origin_cache () const
+    {
+    retry:
+      auto *cache = origin_cache.get_acquire ();
+      if (!cache)
+      {
+        cache = (hb_ot_font_origin_cache_t *) hb_malloc (sizeof (hb_ot_font_origin_cache_t));
+	if (!cache)
+	  return nullptr;
+	new (cache) hb_ot_font_origin_cache_t;
+	return cache;
+      }
+      if (origin_cache.cmpexch (cache, nullptr))
+        return cache;
+      else
+        goto retry;
+    }
+    void release_origin_cache (hb_ot_font_origin_cache_t *cache) const
+    {
+      if (!cache)
+        return;
+      if (!origin_cache.cmpexch (nullptr, cache))
+        hb_free (cache);
+    }
+    void clear_origin_cache () const
+    {
+    retry:
+      auto *cache = origin_cache.get_acquire ();
+      if (!cache)
+	return;
+      if (origin_cache.cmpexch (cache, nullptr))
+	hb_free (cache);
+      else
+        goto retry;
+    }
+
+    OT::hb_scalar_cache_t *acquire_varStore_cache (const OT::ItemVariationStore &varStore) const
+    {
+    retry:
+      auto *cache = varStore_cache.get_acquire ();
+      if (!cache)
+	return varStore.create_cache ();
+      if (varStore_cache.cmpexch (cache, nullptr))
+	return cache;
+      else
+	goto retry;
+    }
+    void release_varStore_cache (OT::hb_scalar_cache_t *cache) const
+    {
+      if (!cache)
+	return;
+      if (!varStore_cache.cmpexch (nullptr, cache))
+	OT::ItemVariationStore::destroy_cache (cache);
+    }
+    void clear_varStore_cache () const
+    {
+    retry:
+      auto *cache = varStore_cache.get_acquire ();
+      if (!cache)
+	return;
+      if (varStore_cache.cmpexch (cache, nullptr))
+	OT::ItemVariationStore::destroy_cache (cache);
+      else
+	goto retry;
+    }
+
+    void clear () const
+    {
+      clear_origin_cache ();
+      clear_varStore_cache ();
+    }
+  } v_origin;
+
+  struct draw_cache_t
+  {
+    mutable hb_atomic_t<OT::hb_scalar_cache_t *> gvar_cache;
+
+    ~draw_cache_t ()
+    {
+      clear ();
+    }
+
+    OT::hb_scalar_cache_t *acquire_gvar_cache (const OT::gvar_accelerator_t &gvar) const
+    {
+    retry:
+      auto *cache = gvar_cache.get_acquire ();
+      if (!cache)
+	return gvar.create_cache ();
+      if (gvar_cache.cmpexch (cache, nullptr))
+	return cache;
+      else
+	goto retry;
+    }
+    void release_gvar_cache (OT::hb_scalar_cache_t *cache) const
+    {
+      if (!cache)
+	return;
+      if (!gvar_cache.cmpexch (nullptr, cache))
+	OT::gvar_accelerator_t::destroy_cache (cache);
+    }
+    void clear_gvar_cache () const
+    {
+    retry:
+      auto *cache = gvar_cache.get_acquire ();
+      if (!cache)
+	return;
+      if (gvar_cache.cmpexch (cache, nullptr))
+	OT::gvar_accelerator_t::destroy_cache (cache);
+      else
+	goto retry;
+    }
+
+    void clear () const
+    {
+      clear_gvar_cache ();
+    }
+  } draw;
+
   void check_serial (hb_font_t *font) const
   {
     int font_serial = font->serial_coords.get_acquire ();
+    if (cached_serial.get_acquire () != font_serial)
+    {
+      /* These caches are dependent on scale and synthetic settings.
+       * Any change to the font invalidates them. */
+      v_origin.clear ();
 
-    if (cached_coords_serial.get_acquire () == font_serial)
-      return;
+      cached_serial.set_release (font_serial);
+    }
 
-    h.clear ();
-    v.clear ();
+    int font_serial_coords = font->serial_coords.get_acquire ();
+    if (cached_coords_serial.get_acquire () != font_serial_coords)
+    {
+      /* These caches are independent of scale or synthetic settings.
+       * Just variation changes will invalidate them. */
+      h.clear ();
+      v.clear ();
+      draw.clear ();
 
-    cached_coords_serial.set_release (font_serial);
+      cached_coords_serial.set_release (font_serial_coords);
+    }
   }
 };
 
@@ -242,37 +387,87 @@ hb_ot_get_glyph_h_advances (hb_font_t* font, void* font_data,
 			    unsigned advance_stride,
 			    void *user_data HB_UNUSED)
 {
+  // Duplicated in v_advances. Ugly. Keep in sync'ish.
 
   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 OT::hmtx_accelerator_t &hmtx = *ot_face->hmtx;
 
-  ot_font->check_serial (font);
-  const OT::HVAR &HVAR = *hmtx.var_table;
-  const OT::ItemVariationStore &varStore = &HVAR + HVAR.varStore;
-  OT::ItemVariationStore::cache_t *varStore_cache = ot_font->h.acquire_varStore_cache (varStore);
+  if (unlikely (!hmtx.has_data ()))
+  {
+    hb_position_t advance = font->face->get_upem () / 2;
+    advance = font->em_scale_x (advance);
+    for (unsigned int i = 0; i < count; i++)
+    {
+      *first_advance = advance;
+      first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
+    }
+    return;
+  }
+
+#ifndef HB_NO_VAR
+  if (!font->has_nonzero_coords)
+  {
+  fallback:
+#else
+  {
+#endif
+    // Just plain htmx data. No need to cache.
+    for (unsigned int i = 0; i < count; i++)
+    {
+      *first_advance = font->em_scale_x (hmtx.get_advance_without_var_unscaled (*first_glyph));
+      first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (first_glyph, glyph_stride);
+      first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
+    }
+    return;
+  }
 
-  hb_ot_font_advance_cache_t *advance_cache = nullptr;
+#ifndef HB_NO_VAR
+  /* has_nonzero_coords. */
 
-  bool use_cache = font->num_coords;
-  if (use_cache)
+  ot_font->check_serial (font);
+  hb_ot_font_advance_cache_t *advance_cache = ot_font->h.acquire_advance_cache ();
+  if (!advance_cache)
   {
-    advance_cache = ot_font->h.acquire_advance_cache ();
-    if (!advance_cache)
-      use_cache = false;
+    // malloc failure. Just use the fallback non-variable path.
+    goto fallback;
   }
 
-  if (!use_cache)
+  /* If HVAR is present, use it.*/
+  const OT::HVAR &HVAR = *hmtx.var_table;
+  if (HVAR.has_data ())
   {
+    const OT::ItemVariationStore &varStore = &HVAR + HVAR.varStore;
+    OT::hb_scalar_cache_t *varStore_cache = ot_font->h.acquire_varStore_cache (varStore);
+
     for (unsigned int i = 0; i < count; i++)
     {
-      *first_advance = font->em_scale_x (hmtx.get_advance_with_var_unscaled (*first_glyph, font, varStore_cache));
+      hb_position_t v;
+      unsigned cv;
+      if (advance_cache->get (*first_glyph, &cv))
+	v = cv;
+      else
+      {
+        v = hmtx.get_advance_with_var_unscaled (*first_glyph, font, varStore_cache);
+	advance_cache->set (*first_glyph, v);
+      }
+      *first_advance = font->em_scale_x (v);
       first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (first_glyph, glyph_stride);
       first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
     }
+
+    ot_font->h.release_varStore_cache (varStore_cache);
+    ot_font->h.release_advance_cache (advance_cache);
+    return;
   }
-  else
-  { /* Use cache. */
+
+  const auto &gvar = *ot_face->gvar;
+  if (gvar.has_data ())
+  {
+    const auto &glyf = *ot_face->glyf;
+    auto *scratch = glyf.acquire_scratch ();
+    OT::hb_scalar_cache_t *gvar_cache = ot_font->draw.acquire_gvar_cache (gvar);
+
     for (unsigned int i = 0; i < count; i++)
     {
       hb_position_t v;
@@ -281,7 +476,7 @@ hb_ot_get_glyph_h_advances (hb_font_t* font, void* font_data,
 	v = cv;
       else
       {
-        v = hmtx.get_advance_with_var_unscaled (*first_glyph, font, varStore_cache);
+        v = glyf.get_advance_with_var_unscaled (*first_glyph, font, false, *scratch, gvar_cache);
 	advance_cache->set (*first_glyph, v);
       }
       *first_advance = font->em_scale_x (v);
@@ -289,10 +484,16 @@ hb_ot_get_glyph_h_advances (hb_font_t* font, void* font_data,
       first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
     }
 
+    ot_font->draw.release_gvar_cache (gvar_cache);
+    glyf.release_scratch (scratch);
     ot_font->h.release_advance_cache (advance_cache);
+    return;
   }
 
-  ot_font->h.release_varStore_cache (varStore_cache);
+  ot_font->h.release_advance_cache (advance_cache);
+  // No HVAR or GVAR.  Just use the fallback non-variable path.
+  goto fallback;
+#endif
 }
 
 #ifndef HB_NO_VERTICAL
@@ -305,99 +506,281 @@ hb_ot_get_glyph_v_advances (hb_font_t* font, void* font_data,
 			    unsigned advance_stride,
 			    void *user_data HB_UNUSED)
 {
+  // Duplicated from h_advances. Ugly. Keep in sync'ish.
+
   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 OT::vmtx_accelerator_t &vmtx = *ot_face->vmtx;
 
-  if (vmtx.has_data ())
+  if (unlikely (!vmtx.has_data ()))
+  {
+    hb_font_extents_t font_extents;
+    font->get_h_extents_with_fallback (&font_extents);
+    hb_position_t advance = font_extents.ascender - font_extents.descender;
+    advance = font->em_scale_y (- (int) advance);
+    for (unsigned int i = 0; i < count; i++)
+    {
+      *first_advance = advance;
+      first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
+    }
+    return;
+  }
+
+#ifndef HB_NO_VAR
+  if (!font->has_nonzero_coords)
+  {
+  fallback:
+#else
+  {
+#endif
+    // Just plain vtmx data. No need to cache.
+    for (unsigned int i = 0; i < count; i++)
+    {
+      *first_advance = font->em_scale_y (- (int) vmtx.get_advance_without_var_unscaled (*first_glyph));
+      first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (first_glyph, glyph_stride);
+      first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
+    }
+    return;
+  }
+
+#ifndef HB_NO_VAR
+  /* has_nonzero_coords. */
+
+  ot_font->check_serial (font);
+  hb_ot_font_advance_cache_t *advance_cache = ot_font->v.acquire_advance_cache ();
+  if (!advance_cache)
+  {
+    // malloc failure. Just use the fallback non-variable path.
+    goto fallback;
+  }
+
+  /* If VVAR is present, use it.*/
+  const OT::VVAR &VVAR = *vmtx.var_table;
+  if (VVAR.has_data ())
   {
-    ot_font->check_serial (font);
-    const OT::VVAR &VVAR = *vmtx.var_table;
     const OT::ItemVariationStore &varStore = &VVAR + VVAR.varStore;
-    OT::ItemVariationStore::cache_t *varStore_cache = ot_font->v.acquire_varStore_cache (varStore);
-    // TODO Use advance_cache.
+    OT::hb_scalar_cache_t *varStore_cache = ot_font->v.acquire_varStore_cache (varStore);
 
     for (unsigned int i = 0; i < count; i++)
     {
-      *first_advance = font->em_scale_y (-(int) vmtx.get_advance_with_var_unscaled (*first_glyph, font, varStore_cache));
+      hb_position_t v;
+      unsigned cv;
+      if (advance_cache->get (*first_glyph, &cv))
+	v = cv;
+      else
+      {
+        v = vmtx.get_advance_with_var_unscaled (*first_glyph, font, varStore_cache);
+	advance_cache->set (*first_glyph, v);
+      }
+      *first_advance = font->em_scale_y (- (int) v);
       first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (first_glyph, glyph_stride);
       first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
     }
 
     ot_font->v.release_varStore_cache (varStore_cache);
+    ot_font->v.release_advance_cache (advance_cache);
+    return;
   }
-  else
+
+  const auto &gvar = *ot_face->gvar;
+  if (gvar.has_data ())
   {
-    hb_font_extents_t font_extents;
-    font->get_h_extents_with_fallback (&font_extents);
-    hb_position_t advance = -(font_extents.ascender - font_extents.descender);
+    const auto &glyf = *ot_face->glyf;
+    auto *scratch = glyf.acquire_scratch ();
+    OT::hb_scalar_cache_t *gvar_cache = ot_font->draw.acquire_gvar_cache (gvar);
 
     for (unsigned int i = 0; i < count; i++)
     {
-      *first_advance = advance;
+      hb_position_t v;
+      unsigned cv;
+      if (advance_cache->get (*first_glyph, &cv))
+	v = cv;
+      else
+      {
+        v = glyf.get_advance_with_var_unscaled (*first_glyph, font, true, *scratch, gvar_cache);
+	advance_cache->set (*first_glyph, v);
+      }
+      *first_advance = font->em_scale_y (- (int) v);
       first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (first_glyph, glyph_stride);
       first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
     }
+
+    ot_font->draw.release_gvar_cache (gvar_cache);
+    glyf.release_scratch (scratch);
+    ot_font->v.release_advance_cache (advance_cache);
+    return;
   }
+
+  ot_font->v.release_advance_cache (advance_cache);
+  // No VVAR or GVAR.  Just use the fallback non-variable path.
+  goto fallback;
+#endif
 }
 #endif
 
 #ifndef HB_NO_VERTICAL
+HB_HOT
 static hb_bool_t
-hb_ot_get_glyph_v_origin (hb_font_t *font,
-			  void *font_data,
-			  hb_codepoint_t glyph,
-			  hb_position_t *x,
-			  hb_position_t *y,
-			  void *user_data HB_UNUSED)
+hb_ot_get_glyph_v_origins (hb_font_t *font,
+			   void *font_data,
+			   unsigned int count,
+			   const hb_codepoint_t *first_glyph,
+			   unsigned glyph_stride,
+			   hb_position_t *first_x,
+			   unsigned x_stride,
+			   hb_position_t *first_y,
+			   unsigned y_stride,
+			   void *user_data HB_UNUSED)
 {
   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;
 
-  *x = font->get_glyph_h_advance (glyph) / 2;
+  /* First, set all the x values to half the advance width. */
+  font->get_glyph_h_advances (count,
+			      first_glyph, glyph_stride,
+			      first_x, x_stride);
+  for (unsigned i = 0; i < count; i++)
+  {
+    *first_x /= 2;
+    first_x = &StructAtOffsetUnaligned<hb_position_t> (first_x, x_stride);
+  }
+
+  /* The vertical origin business is messy...
+   *
+   * We allocate the cache, then have various code paths that use the cache.
+   * Each one is responsible to free it before returning.
+   */
+  hb_ot_font_origin_cache_t *origin_cache = ot_font->v_origin.acquire_origin_cache ();
 
+  /* If there is VORG, always use it. It uses VVAR for variations if necessary. */
   const OT::VORG &VORG = *ot_face->VORG;
-  if (VORG.has_data ())
+  if (origin_cache && VORG.has_data ())
   {
-    float delta = 0;
-
 #ifndef HB_NO_VAR
-    const OT::vmtx_accelerator_t &vmtx = *ot_face->vmtx;
-    const OT::VVAR &VVAR = *vmtx.var_table;
-    if (font->num_coords)
-      VVAR.get_vorg_delta_unscaled (glyph,
-				    font->coords, font->num_coords,
-				    &delta);
+    if (!font->has_nonzero_coords)
 #endif
-
-    *y = font->em_scalef_y (VORG.get_y_origin (glyph) + delta);
+    {
+      for (unsigned i = 0; i < count; i++)
+      {
+	hb_position_t origin;
+	unsigned cv;
+	if (origin_cache->get (*first_glyph, &cv))
+	  origin = font->y_scale < 0 ? -cv : cv;
+	else
+	{
+	  origin = font->em_scalef_y (VORG.get_y_origin (*first_glyph));
+	  origin_cache->set (*first_glyph, font->y_scale < 0 ? -origin : origin);
+	}
+
+	*first_y = origin;
+
+	first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (first_glyph, glyph_stride);
+	first_y = &StructAtOffsetUnaligned<hb_position_t> (first_y, y_stride);
+      }
+    }
+#ifndef HB_NO_VAR
+    else
+    {
+      const OT::VVAR &VVAR = *ot_face->vmtx->var_table;
+      const auto &varStore = &VVAR + VVAR.varStore;
+      auto *varStore_cache = ot_font->v_origin.acquire_varStore_cache (varStore);
+      for (unsigned i = 0; i < count; i++)
+      {
+	hb_position_t origin;
+	unsigned cv;
+	if (origin_cache->get (*first_glyph, &cv))
+	  origin = font->y_scale < 0 ? -cv : cv;
+	else
+	{
+	  origin = font->em_scalef_y (VORG.get_y_origin (*first_glyph) +
+				      VVAR.get_vorg_delta_unscaled (*first_glyph,
+								    font->coords, font->num_coords,
+								    varStore_cache));
+	  origin_cache->set (*first_glyph, font->y_scale < 0 ? -origin : origin);
+	}
+
+	*first_y = origin;
+
+	first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (first_glyph, glyph_stride);
+	first_y = &StructAtOffsetUnaligned<hb_position_t> (first_y, y_stride);
+      }
+      ot_font->v_origin.release_varStore_cache (varStore_cache);
+    }
+#endif
+    ot_font->v_origin.release_origin_cache (origin_cache);
     return true;
   }
 
-  hb_glyph_extents_t extents = {0};
-
-  if (hb_font_get_glyph_extents (font, glyph, &extents))
+  /* If and only if `vmtx` is present and it's a `glyf` font,
+   * we use the top phantom point, deduced from vmtx,glyf[,gvar]. */
+  const auto &vmtx = *ot_face->vmtx;
+  const auto &glyf = *ot_face->glyf;
+  if (origin_cache && vmtx.has_data() && glyf.has_data ())
   {
-    const OT::vmtx_accelerator_t &vmtx = *ot_face->vmtx;
-    int tsb = 0;
-    if (vmtx.get_leading_bearing_with_var_unscaled (font, glyph, &tsb))
+    auto *scratch = glyf.acquire_scratch ();
+    OT::hb_scalar_cache_t *gvar_cache = font->has_nonzero_coords ?
+					ot_font->draw.acquire_gvar_cache (*ot_face->gvar) :
+					nullptr;
+
+    for (unsigned i = 0; i < count; i++)
     {
-      *y = extents.y_bearing + font->em_scale_y (tsb);
-      return true;
+      hb_position_t origin;
+      unsigned cv;
+      if (origin_cache->get (*first_glyph, &cv))
+	origin = font->y_scale < 0 ? -cv : cv;
+      else
+      {
+	origin = font->em_scalef_y (glyf.get_v_origin_with_var_unscaled (*first_glyph, font, *scratch, gvar_cache));
+	origin_cache->set (*first_glyph, font->y_scale < 0 ? -origin : origin);
+      }
+
+      *first_y = origin;
+
+      first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (first_glyph, glyph_stride);
+      first_y = &StructAtOffsetUnaligned<hb_position_t> (first_y, y_stride);
     }
 
-    hb_font_extents_t font_extents;
-    font->get_h_extents_with_fallback (&font_extents);
-    hb_position_t advance = font_extents.ascender - font_extents.descender;
-    hb_position_t diff = advance - -extents.height;
-    *y = extents.y_bearing + (diff >> 1);
+    if (gvar_cache)
+      ot_font->draw.release_gvar_cache (gvar_cache);
+    glyf.release_scratch (scratch);
+    ot_font->v_origin.release_origin_cache (origin_cache);
     return true;
   }
 
-  hb_font_extents_t font_extents;
-  font->get_h_extents_with_fallback (&font_extents);
-  *y = font_extents.ascender;
+  /* Otherwise, use glyph extents to center the glyph vertically.
+   * If getting glyph extents failed, just use the font ascender. */
+  if (origin_cache && font->has_glyph_extents_func ())
+  {
+    hb_font_extents_t font_extents;
+    font->get_h_extents_with_fallback (&font_extents);
+    hb_position_t font_advance = font_extents.ascender - font_extents.descender;
+
+    for (unsigned i = 0; i < count; i++)
+    {
+      hb_position_t origin;
+      unsigned cv;
+
+      if (origin_cache->get (*first_glyph, &cv))
+	origin = font->y_scale < 0 ? -cv : cv;
+      else
+      {
+	hb_glyph_extents_t extents = {0};
+	if (likely (font->get_glyph_extents (*first_glyph, &extents)))
+	  origin = extents.y_bearing + ((font_advance - -extents.height) >> 1);
+	else
+	  origin = font_extents.ascender;
+
+	origin_cache->set (*first_glyph, font->y_scale < 0 ? -origin : origin);
+      }
+
+      *first_y = origin;
+
+      first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (first_glyph, glyph_stride);
+      first_y = &StructAtOffsetUnaligned<hb_position_t> (first_y, y_stride);
+    }
+  }
 
+  ot_font->v_origin.release_origin_cache (origin_cache);
   return true;
 }
 #endif
@@ -498,17 +881,33 @@ hb_ot_draw_glyph_or_fail (hb_font_t *font,
 			  hb_draw_funcs_t *draw_funcs, void *draw_data,
 			  void *user_data)
 {
+  const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data;
   hb_draw_session_t draw_session {draw_funcs, draw_data};
+  bool ret = false;
+
+  OT::hb_scalar_cache_t *gvar_cache = nullptr;
+  if (font->num_coords)
+  {
+    ot_font->check_serial (font);
+    gvar_cache = ot_font->draw.acquire_gvar_cache (*ot_font->ot_face->gvar);
+  }
+
 #ifndef HB_NO_VAR_COMPOSITES
-  if (font->face->table.VARC->get_path (font, glyph, draw_session)) return true;
+  if (font->face->table.VARC->get_path (font, glyph, draw_session)) { ret = true; goto done; }
 #endif
   // Keep the following in synch with VARC::get_path_at()
-  if (font->face->table.glyf->get_path (font, glyph, draw_session)) return true;
+  if (font->face->table.glyf->get_path (font, glyph, draw_session, gvar_cache)) { ret = true; goto done; }
+
 #ifndef HB_NO_CFF
-  if (font->face->table.cff2->get_path (font, glyph, draw_session)) return true;
-  if (font->face->table.cff1->get_path (font, glyph, draw_session)) return true;
+  if (font->face->table.cff2->get_path (font, glyph, draw_session)) { ret = true; goto done; }
+  if (font->face->table.cff1->get_path (font, glyph, draw_session)) { ret = true; goto done; }
 #endif
-  return false;
+
+done:
+
+  ot_font->draw.release_gvar_cache (gvar_cache);
+
+  return ret;
 }
 #endif
 
@@ -548,12 +947,11 @@ static struct hb_ot_font_funcs_lazy_loader_t : hb_font_funcs_lazy_loader_t<hb_ot
 
     hb_font_funcs_set_font_h_extents_func (funcs, hb_ot_get_font_h_extents, nullptr, nullptr);
     hb_font_funcs_set_glyph_h_advances_func (funcs, hb_ot_get_glyph_h_advances, nullptr, nullptr);
-    //hb_font_funcs_set_glyph_h_origin_func (funcs, hb_ot_get_glyph_h_origin, nullptr, nullptr);
 
 #ifndef HB_NO_VERTICAL
     hb_font_funcs_set_font_v_extents_func (funcs, hb_ot_get_font_v_extents, nullptr, nullptr);
     hb_font_funcs_set_glyph_v_advances_func (funcs, hb_ot_get_glyph_v_advances, nullptr, nullptr);
-    hb_font_funcs_set_glyph_v_origin_func (funcs, hb_ot_get_glyph_v_origin, nullptr, nullptr);
+    hb_font_funcs_set_glyph_v_origins_func (funcs, hb_ot_get_glyph_v_origins, nullptr, nullptr);
 #endif
 
 #ifndef HB_NO_DRAW

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

@@ -45,16 +45,6 @@
 #define HB_OT_TAG_vmtx HB_TAG('v','m','t','x')
 
 
-HB_INTERNAL bool
-_glyf_get_leading_bearing_with_var_unscaled (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical, int *lsb);
-
-HB_INTERNAL unsigned
-_glyf_get_advance_with_var_unscaled (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical);
-
-HB_INTERNAL bool
-_glyf_get_leading_bearing_without_var_unscaled (hb_face_t *face, hb_codepoint_t gid, bool is_vertical, int *lsb);
-
-
 namespace OT {
 
 
@@ -237,7 +227,7 @@ struct hmtxvmtx
 
     auto it =
     + hb_iter (c->plan->new_to_old_gid_list)
-    | hb_map ([c, &_mtx, mtx_map] (hb_codepoint_pair_t _)
+    | hb_map ([&_mtx, mtx_map] (hb_codepoint_pair_t _)
 	      {
 		hb_codepoint_t new_gid = _.first;
 		hb_codepoint_t old_gid = _.second;
@@ -246,8 +236,7 @@ struct hmtxvmtx
 		if (!mtx_map->has (new_gid, &v))
 		{
 		  int lsb = 0;
-		  if (!_mtx.get_leading_bearing_without_var_unscaled (old_gid, &lsb))
-		    (void) _glyf_get_leading_bearing_without_var_unscaled (c->plan->source, old_gid, !T::is_horizontal, &lsb);
+		  _mtx.get_leading_bearing_without_var_unscaled (old_gid, &lsb);
 		  return hb_pair (_mtx.get_advance_without_var_unscaled (old_gid), +lsb);
 		}
 		return *v;
@@ -326,49 +315,23 @@ struct hmtxvmtx
 
     bool has_data () const { return (bool) num_bearings; }
 
-    bool get_leading_bearing_without_var_unscaled (hb_codepoint_t glyph,
+    void get_leading_bearing_without_var_unscaled (hb_codepoint_t glyph,
 						   int *lsb) const
     {
       if (glyph < num_long_metrics)
       {
 	*lsb = table->longMetricZ[glyph].sb;
-	return true;
+	return;
       }
 
       if (unlikely (glyph >= num_bearings))
-	return false;
-
-      const FWORD *bearings = (const FWORD *) &table->longMetricZ[num_long_metrics];
-      *lsb = bearings[glyph - num_long_metrics];
-      return true;
-    }
-
-    bool get_leading_bearing_with_var_unscaled (hb_font_t *font,
-						hb_codepoint_t glyph,
-						int *lsb) const
-    {
-      if (!font->num_coords)
-	return get_leading_bearing_without_var_unscaled (glyph, lsb);
-
-#ifndef HB_NO_VAR
-      float delta;
-      if (var_table->get_lsb_delta_unscaled (glyph, font->coords, font->num_coords, &delta) &&
-	  get_leading_bearing_without_var_unscaled (glyph, lsb))
       {
-	*lsb += roundf (delta);
-	return true;
+        *lsb = 0;
+	return;
       }
 
-      // If there's no vmtx data, the phantom points from glyf table are not accurate,
-      // so we cannot take the next path.
-      bool is_vertical = T::tableTag == HB_OT_TAG_vmtx;
-      if (is_vertical && !has_data ())
-        return false;
-
-      return _glyf_get_leading_bearing_with_var_unscaled (font, glyph, is_vertical, lsb);
-#else
-      return false;
-#endif
+      const FWORD *bearings = (const FWORD *) &table->longMetricZ[num_long_metrics];
+      *lsb = bearings[glyph - num_long_metrics];
     }
 
     unsigned int get_advance_without_var_unscaled (hb_codepoint_t glyph) const
@@ -402,27 +365,17 @@ struct hmtxvmtx
       return advances[hb_min (glyph - num_bearings, num_advances - num_bearings - 1)];
     }
 
-    unsigned get_advance_with_var_unscaled (hb_codepoint_t  glyph,
-					    hb_font_t      *font,
-					    ItemVariationStore::cache_t *store_cache = nullptr) const
+#ifndef HB_NO_VAR
+    unsigned get_advance_with_var_unscaled (hb_codepoint_t     glyph,
+					    hb_font_t         *font,
+					    hb_scalar_cache_t *store_cache = nullptr) const
     {
       unsigned int advance = get_advance_without_var_unscaled (glyph);
-
-#ifndef HB_NO_VAR
-      if (unlikely (glyph >= num_bearings) || !font->num_coords)
-	return advance;
-
-      if (var_table.get_length ())
-	return advance + roundf (var_table->get_advance_delta_unscaled (glyph,
-									font->coords, font->num_coords,
-									store_cache));
-
-      unsigned glyf_advance = _glyf_get_advance_with_var_unscaled (font, glyph, T::tableTag == HB_OT_TAG_vmtx);
-      return glyf_advance ? glyf_advance : advance;
-#else
-      return advance;
-#endif
+      return advance + roundf (var_table->get_advance_delta_unscaled (glyph,
+								      font->coords, font->num_coords,
+								      store_cache));
     }
+#endif
 
     protected:
     // 0 <= num_long_metrics <= num_bearings <= num_advances <= num_glyphs

+ 207 - 123
thirdparty/harfbuzz/src/hb-ot-layout-common.hh

@@ -2548,32 +2548,94 @@ struct SparseVarRegionAxis
   DEFINE_SIZE_STATIC (8);
 };
 
-#define REGION_CACHE_ITEM_CACHE_INVALID INT_MIN
-#define REGION_CACHE_ITEM_MULTIPLIER (float (1 << ((sizeof (int) * 8) - 2)))
-#define REGION_CACHE_ITEM_DIVISOR (1.f / float (1 << ((sizeof (int) * 8) - 2)))
-
-struct VarRegionList
+struct hb_scalar_cache_t
 {
-  using cache_t = hb_atomic_t<int>;
+  private:
+  static constexpr unsigned STATIC_LENGTH = 16;
+  static constexpr int INVALID = INT_MIN;
+  static constexpr float MULTIPLIER = 1 << ((sizeof (int) * 8) - 2);
+  static constexpr float DIVISOR = 1.f / MULTIPLIER;
 
-  float evaluate (unsigned int region_index,
-		  const int *coords, unsigned int coord_len,
-		  cache_t *cache = nullptr) const
+  public:
+  hb_scalar_cache_t () : length (STATIC_LENGTH) { clear (); }
+
+  hb_scalar_cache_t (const hb_scalar_cache_t&) = delete;
+  hb_scalar_cache_t (hb_scalar_cache_t&&) = delete;
+  hb_scalar_cache_t& operator= (const hb_scalar_cache_t&) = delete;
+  hb_scalar_cache_t& operator= (hb_scalar_cache_t&&) = delete;
+
+  static hb_scalar_cache_t *create (unsigned int count,
+				    hb_scalar_cache_t *scratch_cache = nullptr)
   {
-    if (unlikely (region_index >= regionCount))
-      return 0.;
+    if (!count) return (hb_scalar_cache_t *) &Null(hb_scalar_cache_t);
 
-    cache_t *cached_value = nullptr;
-    if (cache)
+    if (scratch_cache && count <= scratch_cache->length)
     {
-      cached_value = &(cache[region_index]);
-      if (*cached_value != REGION_CACHE_ITEM_CACHE_INVALID)
-	return *cached_value * REGION_CACHE_ITEM_DIVISOR;
+      scratch_cache->clear ();
+      return scratch_cache;
     }
 
+    auto *cache = (hb_scalar_cache_t *) hb_malloc (sizeof (hb_scalar_cache_t) - sizeof (values) + sizeof (values[0]) * count);
+    if (unlikely (!cache)) return (hb_scalar_cache_t *) &Null(hb_scalar_cache_t);
+
+    cache->length = count;
+    cache->clear ();
+
+    return cache;
+  }
+
+  static void destroy (hb_scalar_cache_t *cache,
+		       hb_scalar_cache_t *scratch_cache = nullptr)
+  {
+    if (cache != &Null(hb_scalar_cache_t) && cache != scratch_cache)
+      hb_free (cache);
+  }
+
+  void clear ()
+  {
+    for (unsigned i = 0; i < length; i++)
+      values[i] = INVALID;
+  }
+
+  HB_ALWAYS_INLINE
+  bool get (unsigned i, float *value) const
+  {
+    if (unlikely (i >= length))
+    {
+      *value = 0.f;
+      return true;
+    }
+    auto *cached_value = &values[i];
+    if (*cached_value != INVALID)
+    {
+      *value = *cached_value ? *cached_value * DIVISOR : 0.f;
+      return true;
+    }
+    return false;
+  }
+
+  HB_ALWAYS_INLINE
+  void set (unsigned i, float value)
+  {
+    if (unlikely (i >= length)) return;
+    auto *cached_value = &values[i];
+    *cached_value = roundf(value * MULTIPLIER);
+  }
+
+  private:
+  unsigned length;
+  mutable hb_atomic_t<int> values[STATIC_LENGTH];
+};
+
+struct VarRegionList
+{
+  private:
+  float evaluate_impl (unsigned int region_index,
+		       const int *coords, unsigned int coord_len) const
+  {
     const VarRegionAxis *axes = axesZ.arrayZ + (region_index * axisCount);
+    float v = 1.f;
 
-    float v = 1.;
     unsigned int count = axisCount;
     for (unsigned int i = 0; i < count; i++)
     {
@@ -2581,15 +2643,32 @@ struct VarRegionList
       float factor = axes[i].evaluate (coord);
       if (factor == 0.f)
       {
-        if (cache)
-	  *cached_value = 0.;
-	return 0.;
+	v = 0.f;
+	break;
       }
       v *= factor;
     }
 
+    return v;
+  }
+
+  public:
+  HB_ALWAYS_INLINE
+  float evaluate (unsigned int region_index,
+		  const int *coords, unsigned int coord_len,
+		  hb_scalar_cache_t *cache = nullptr) const
+  {
+    if (unlikely (region_index >= regionCount))
+      return 0.;
+
+    float v;
+    if (cache && cache->get (region_index, &v))
+      return v;
+
+    v = evaluate_impl (region_index, coords, coord_len);
+
     if (cache)
-      *cached_value = v * REGION_CACHE_ITEM_MULTIPLIER;
+      cache->set (region_index, v);
     return v;
   }
 
@@ -2732,29 +2811,24 @@ struct SparseVariationRegion : Array16Of<SparseVarRegionAxis>
 
 struct SparseVarRegionList
 {
-  using cache_t = hb_atomic_t<int>;
-
+  HB_ALWAYS_INLINE
   float evaluate (unsigned int region_index,
 		  const int *coords, unsigned int coord_len,
-		  cache_t *cache = nullptr) const
+		  hb_scalar_cache_t *cache = nullptr) const
   {
     if (unlikely (region_index >= regions.len))
       return 0.;
 
-    cache_t *cached_value = nullptr;
-    if (cache)
-    {
-      cached_value = &(cache[region_index]);
-      if (*cached_value != REGION_CACHE_ITEM_CACHE_INVALID)
-	return *cached_value * REGION_CACHE_ITEM_DIVISOR;
-    }
+    float v;
+    if (cache && cache->get (region_index, &v))
+      return v;
 
     const SparseVariationRegion &region = this+regions[region_index];
 
-    float v = region.evaluate (coords, coord_len);
-
+    v = region.evaluate (coords, coord_len);
     if (cache)
-      *cached_value = v * REGION_CACHE_ITEM_MULTIPLIER;
+      cache->set (region_index, v);
+
     return v;
   }
 
@@ -2792,46 +2866,62 @@ struct VarData
 	 + itemCount * get_row_size ();
   }
 
-  float get_delta (unsigned int inner,
-		   const int *coords, unsigned int coord_count,
-		   const VarRegionList &regions,
-		   VarRegionList::cache_t *cache = nullptr) const
+  float _get_delta (unsigned int inner,
+		    const int *coords, unsigned int coord_count,
+		    const VarRegionList &regions,
+		    hb_scalar_cache_t *cache = nullptr) const
   {
     if (unlikely (inner >= itemCount))
       return 0.;
+    bool is_long = longWords ();
+    unsigned int count = regionIndices.len;
+    unsigned word_count = wordCount ();
+    unsigned int scount = is_long ? count : word_count;
+    unsigned int lcount = is_long ? word_count : 0;
 
-   unsigned int count = regionIndices.len;
-   bool is_long = longWords ();
-   unsigned word_count = wordCount ();
-   unsigned int scount = is_long ? count : word_count;
-   unsigned int lcount = is_long ? word_count : 0;
+    const HBUINT8 *bytes = get_delta_bytes ();
+    const HBUINT8 *row = bytes + inner * get_row_size ();
 
-   const HBUINT8 *bytes = get_delta_bytes ();
-   const HBUINT8 *row = bytes + inner * get_row_size ();
+    float delta = 0.;
+    unsigned int i = 0;
 
-   float delta = 0.;
-   unsigned int i = 0;
+    const HBINT32 *lcursor = reinterpret_cast<const HBINT32 *> (row);
+    for (; i < lcount; i++)
+    {
+      float scalar = regions.evaluate (regionIndices.arrayZ[i], coords, coord_count, cache);
+      if (scalar)
+        delta += scalar * *lcursor;
+      lcursor++;
+    }
+    const HBINT16 *scursor = reinterpret_cast<const HBINT16 *> (lcursor);
+    for (; i < scount; i++)
+    {
+      float scalar = regions.evaluate (regionIndices.arrayZ[i], coords, coord_count, cache);
+      if (scalar)
+       delta += scalar * *scursor;
+      scursor++;
+    }
+    const HBINT8 *bcursor = reinterpret_cast<const HBINT8 *> (scursor);
+    for (; i < count; i++)
+    {
+      float scalar = regions.evaluate (regionIndices.arrayZ[i], coords, coord_count, cache);
+      if (scalar)
+        delta += scalar * *bcursor;
+      bcursor++;
+    }
 
-   const HBINT32 *lcursor = reinterpret_cast<const HBINT32 *> (row);
-   for (; i < lcount; i++)
-   {
-     float scalar = regions.evaluate (regionIndices.arrayZ[i], coords, coord_count, cache);
-     delta += scalar * *lcursor++;
-   }
-   const HBINT16 *scursor = reinterpret_cast<const HBINT16 *> (lcursor);
-   for (; i < scount; i++)
-   {
-     float scalar = regions.evaluate (regionIndices.arrayZ[i], coords, coord_count, cache);
-     delta += scalar * *scursor++;
-   }
-   const HBINT8 *bcursor = reinterpret_cast<const HBINT8 *> (scursor);
-   for (; i < count; i++)
-   {
-     float scalar = regions.evaluate (regionIndices.arrayZ[i], coords, coord_count, cache);
-     delta += scalar * *bcursor++;
-   }
+    return delta;
+  }
 
-   return delta;
+  HB_ALWAYS_INLINE
+  float get_delta (unsigned int inner,
+		   const int *coords, unsigned int coord_count,
+		   const VarRegionList &regions,
+		   hb_scalar_cache_t *cache = nullptr) const
+  {
+    unsigned int count = regionIndices.len;
+    if (!count) return 0.f; // This is quite common, so optimize it.
+    return _get_delta (inner, coords, coord_count, regions, cache);
   }
 
   void get_region_scalars (const int *coords, unsigned int coord_count,
@@ -2869,7 +2959,7 @@ struct VarData
       return false;
     }
 
-    if (unlikely (!c->extend_min (this))) return_trace (false);    
+    if (unlikely (!c->extend_min (this))) return_trace (false);
     itemCount = row_count;
 
     int min_threshold = has_long ? -65536 : -128;
@@ -3150,7 +3240,7 @@ struct MultiVarData
 		  const int *coords, unsigned int coord_count,
 		  const SparseVarRegionList &regions,
 		  hb_array_t<float> out,
-		  SparseVarRegionList::cache_t *cache = nullptr) const
+		  hb_scalar_cache_t *cache = nullptr) const
   {
     auto &deltaSets = StructAfter<decltype (deltaSetsX)> (regionIndices);
 
@@ -3187,31 +3277,24 @@ struct MultiVarData
 struct ItemVariationStore
 {
   friend struct item_variations_t;
-  using cache_t = VarRegionList::cache_t;
 
-  cache_t *create_cache () const
+  hb_scalar_cache_t *create_cache () const
   {
 #ifdef HB_NO_VAR
-    return nullptr;
+    return hb_scalar_cache_t::create (0);
 #endif
-    unsigned count = (this+regions).regionCount;
-    if (!count) return nullptr;
-
-    cache_t *cache = (cache_t *) hb_malloc (sizeof (float) * count);
-    if (unlikely (!cache)) return nullptr;
-
-    for (unsigned i = 0; i < count; i++)
-      cache[i] = REGION_CACHE_ITEM_CACHE_INVALID;
-
-    return cache;
+    return hb_scalar_cache_t::create ((this+regions).regionCount);
   }
 
-  static void destroy_cache (cache_t *cache) { hb_free (cache); }
+  static void destroy_cache (hb_scalar_cache_t *cache)
+  {
+    hb_scalar_cache_t::destroy (cache);
+  }
 
   private:
   float get_delta (unsigned int outer, unsigned int inner,
 		   const int *coords, unsigned int coord_count,
-		   VarRegionList::cache_t *cache = nullptr) const
+		   hb_scalar_cache_t *cache = nullptr) const
   {
 #ifdef HB_NO_VAR
     return 0.f;
@@ -3229,7 +3312,7 @@ struct ItemVariationStore
   public:
   float get_delta (unsigned int index,
 		   const int *coords, unsigned int coord_count,
-		   VarRegionList::cache_t *cache = nullptr) const
+		   hb_scalar_cache_t *cache = nullptr) const
   {
     unsigned int outer = index >> 16;
     unsigned int inner = index & 0xFFFF;
@@ -3237,7 +3320,7 @@ struct ItemVariationStore
   }
   float get_delta (unsigned int index,
 		   hb_array_t<const int> coords,
-		   VarRegionList::cache_t *cache = nullptr) const
+		   hb_scalar_cache_t *cache = nullptr) const
   {
     return get_delta (index,
 		      coords.arrayZ, coords.length,
@@ -3445,43 +3528,28 @@ struct ItemVariationStore
 
 struct MultiItemVariationStore
 {
-  using cache_t = SparseVarRegionList::cache_t;
-
-  cache_t *create_cache (hb_array_t<cache_t> static_cache = hb_array_t<cache_t> ()) const
+  hb_scalar_cache_t *create_cache (hb_scalar_cache_t *static_cache = nullptr) const
   {
 #ifdef HB_NO_VAR
-    return nullptr;
+    return hb_scalar_cache_t::create (0);
 #endif
     auto &r = this+regions;
     unsigned count = r.regions.len;
 
-    cache_t *cache;
-    if (count <= static_cache.length)
-      cache = static_cache.arrayZ;
-    else
-    {
-      cache = (cache_t *) hb_malloc (sizeof (float) * count);
-      if (unlikely (!cache)) return nullptr;
-    }
-
-    for (unsigned i = 0; i < count; i++)
-      cache[i] = REGION_CACHE_ITEM_CACHE_INVALID;
-
-    return cache;
+    return hb_scalar_cache_t::create (count, static_cache);
   }
 
-  static void destroy_cache (cache_t *cache,
-			     hb_array_t<cache_t> static_cache = hb_array_t<cache_t> ())
+  static void destroy_cache (hb_scalar_cache_t *cache,
+			     hb_scalar_cache_t *static_cache = nullptr)
   {
-    if (cache != static_cache.arrayZ)
-      hb_free (cache);
+    hb_scalar_cache_t::destroy (cache, static_cache);
   }
 
   private:
   void get_delta (unsigned int outer, unsigned int inner,
 		  const int *coords, unsigned int coord_count,
 		  hb_array_t<float> out,
-		  VarRegionList::cache_t *cache = nullptr) const
+		  hb_scalar_cache_t *cache = nullptr) const
   {
 #ifdef HB_NO_VAR
     return;
@@ -3501,7 +3569,7 @@ struct MultiItemVariationStore
   void get_delta (unsigned int index,
 		  const int *coords, unsigned int coord_count,
 		  hb_array_t<float> out,
-		  VarRegionList::cache_t *cache = nullptr) const
+		  hb_scalar_cache_t *cache = nullptr) const
   {
     unsigned int outer = index >> 16;
     unsigned int inner = index & 0xFFFF;
@@ -3510,7 +3578,7 @@ struct MultiItemVariationStore
   void get_delta (unsigned int index,
 		  hb_array_t<const int> coords,
 		  hb_array_t<float> out,
-		  VarRegionList::cache_t *cache = nullptr) const
+		  hb_scalar_cache_t *cache = nullptr) const
   {
     return get_delta (index,
 		      coords.arrayZ, coords.length,
@@ -3540,8 +3608,6 @@ struct MultiItemVariationStore
   DEFINE_SIZE_ARRAY_SIZED (8, dataSets);
 };
 
-#undef REGION_CACHE_ITEM_CACHE_INVALID
-
 template <typename MapCountT>
 struct DeltaSetIndexMapFormat01
 {
@@ -3592,13 +3658,19 @@ struct DeltaSetIndexMapFormat01
     return_trace (true);
   }
 
+  HB_ALWAYS_INLINE
   uint32_t map (unsigned int v) const /* Returns 16.16 outer.inner. */
   {
     /* If count is zero, pass value unchanged.  This takes
      * care of direct mapping for advance map. */
     if (!mapCount)
       return v;
+    return _map (v);
+  }
 
+  HB_HOT
+  uint32_t _map (unsigned int v) const /* Returns 16.16 outer.inner. */
+  {
     if (v >= mapCount)
       v = mapCount - 1;
 
@@ -3736,7 +3808,7 @@ struct ItemVarStoreInstancer
   ItemVarStoreInstancer (const ItemVariationStore *varStore_,
 			 const DeltaSetIndexMap *varIdxMap,
 			 hb_array_t<const int> coords,
-			 VarRegionList::cache_t *cache = nullptr) :
+			 hb_scalar_cache_t *cache = nullptr) :
     varStore (varStore_), varIdxMap (varIdxMap), coords (coords), cache (cache)
   {
     if (!varStore)
@@ -3762,7 +3834,7 @@ struct ItemVarStoreInstancer
   const ItemVariationStore *varStore;
   const DeltaSetIndexMap *varIdxMap;
   hb_array_t<const int> coords;
-  VarRegionList::cache_t *cache;
+  hb_scalar_cache_t *cache;
 };
 
 struct MultiItemVarStoreInstancer
@@ -3770,7 +3842,7 @@ struct MultiItemVarStoreInstancer
   MultiItemVarStoreInstancer (const MultiItemVariationStore *varStore,
 			      const DeltaSetIndexMap *varIdxMap,
 			      hb_array_t<const int> coords,
-			      SparseVarRegionList::cache_t *cache = nullptr) :
+			      hb_scalar_cache_t *cache = nullptr) :
     varStore (varStore), varIdxMap (varIdxMap), coords (coords), cache (cache)
   {
     if (!varStore)
@@ -3803,7 +3875,7 @@ struct MultiItemVarStoreInstancer
   const MultiItemVariationStore *varStore;
   const DeltaSetIndexMap *varIdxMap;
   hb_array_t<const int> coords;
-  SparseVarRegionList::cache_t *cache;
+  hb_scalar_cache_t *cache;
 };
 
 
@@ -4783,13 +4855,13 @@ struct VariationDevice
 
   hb_position_t get_x_delta (hb_font_t *font,
 			     const ItemVariationStore &store,
-			     ItemVariationStore::cache_t *store_cache = nullptr) const
-  { return !font->num_coords ? 0 : font->em_scalef_x (get_delta (font, store, store_cache)); }
+			     hb_scalar_cache_t *store_cache = nullptr) const
+  { return !font->has_nonzero_coords ? 0 : font->em_scalef_x (get_delta (font, store, store_cache)); }
 
   hb_position_t get_y_delta (hb_font_t *font,
 			     const ItemVariationStore &store,
-			     ItemVariationStore::cache_t *store_cache = nullptr) const
-  { return !font->num_coords ? 0 : font->em_scalef_y (get_delta (font, store, store_cache)); }
+			     hb_scalar_cache_t *store_cache = nullptr) const
+  { return !font->has_nonzero_coords ? 0 : font->em_scalef_y (get_delta (font, store, store_cache)); }
 
   VariationDevice* copy (hb_serialize_context_t *c,
                          const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map) const
@@ -4823,9 +4895,9 @@ struct VariationDevice
 
   float get_delta (hb_font_t *font,
 		   const ItemVariationStore &store,
-		   ItemVariationStore::cache_t *store_cache = nullptr) const
+		   hb_scalar_cache_t *store_cache = nullptr) const
   {
-    return store.get_delta (varIdx, font->coords, font->num_coords, (ItemVariationStore::cache_t *) store_cache);
+    return store.get_delta (varIdx, font->coords, font->num_coords, store_cache);
   }
 
   protected:
@@ -4850,7 +4922,7 @@ struct Device
 {
   hb_position_t get_x_delta (hb_font_t *font,
 			     const ItemVariationStore &store=Null (ItemVariationStore),
-			     ItemVariationStore::cache_t *store_cache = nullptr) const
+			     hb_scalar_cache_t *store_cache = nullptr) const
   {
     switch (u.b.format)
     {
@@ -4868,7 +4940,7 @@ struct Device
   }
   hb_position_t get_y_delta (hb_font_t *font,
 			     const ItemVariationStore &store=Null (ItemVariationStore),
-			     ItemVariationStore::cache_t *store_cache = nullptr) const
+			     hb_scalar_cache_t *store_cache = nullptr) const
   {
     switch (u.b.format)
     {
@@ -4954,6 +5026,18 @@ struct Device
     }
   }
 
+  bool is_variation_device () const
+  {
+    switch (u.b.format) {
+#ifndef HB_NO_VAR
+    case 0x8000:
+      return true;
+#endif
+    default:
+      return false;
+    }
+  }
+
   protected:
   union {
   DeviceHeader		b;

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

@@ -700,7 +700,7 @@ struct hb_ot_apply_context_t :
   const GDEF::accelerator_t &gdef_accel;
   const hb_ot_layout_lookup_accelerator_t *lookup_accel = nullptr;
   const ItemVariationStore &var_store;
-  ItemVariationStore::cache_t *var_store_cache;
+  hb_scalar_cache_t *var_store_cache;
   hb_set_digest_t digest;
 
   hb_direction_t direction;
@@ -723,7 +723,7 @@ struct hb_ot_apply_context_t :
 			 hb_font_t *font_,
 			 hb_buffer_t *buffer_,
 			 hb_blob_t *table_blob_,
-			 ItemVariationStore::cache_t *var_store_cache_ = nullptr) :
+			 hb_scalar_cache_t *var_store_cache_ = nullptr) :
 			table_index (table_index_),
 			font (font_), face (font->face), buffer (buffer_),
 			sanitizer (table_blob_),

+ 26 - 26
thirdparty/harfbuzz/src/hb-ot-layout.cc

@@ -343,7 +343,7 @@ hb_ot_layout_get_glyphs_in_class (hb_face_t                  *face,
  * @face: The #hb_face_t to work on
  * @glyph: The #hb_codepoint_t code point to query
  * @start_offset: offset of the first attachment point to retrieve
- * @point_count: (inout) (optional): Input = the maximum number of attachment points to return;
+ * @point_count: (inout) (nullable): Input = the maximum number of attachment points to return;
  *               Output = the actual number of attachment points returned (may be zero)
  * @point_array: (out) (array length=point_count): The array of attachment points found for the query
  *
@@ -373,7 +373,7 @@ hb_ot_layout_get_attach_points (hb_face_t      *face,
  * @direction: The #hb_direction_t text direction to use
  * @glyph: The #hb_codepoint_t code point to query
  * @start_offset: offset of the first caret position to retrieve
- * @caret_count: (inout) (optional): Input = the maximum number of caret positions to return;
+ * @caret_count: (inout) (nullable): Input = the maximum number of caret positions to return;
  *               Output = the actual number of caret positions returned (may be zero)
  * @caret_array: (out) (array length=caret_count): The array of caret positions found for the query
  *
@@ -444,7 +444,7 @@ get_gsubgpos_table (hb_face_t *face,
  * @face: #hb_face_t to work upon
  * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
  * @start_offset: offset of the first script tag to retrieve
- * @script_count: (inout) (optional): Input = the maximum number of script tags to return;
+ * @script_count: (inout) (nullable): Input = the maximum number of script tags to return;
  *                Output = the actual number of script tags returned (may be zero)
  * @script_tags: (out) (array length=script_count): The array of #hb_tag_t script tags found for the query
  *
@@ -541,8 +541,8 @@ hb_ot_layout_table_choose_script (hb_face_t      *face,
  * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
  * @script_count: Number of script tags in the array
  * @script_tags: Array of #hb_tag_t script tags
- * @script_index: (out) (optional): The index of the requested script
- * @chosen_script: (out) (optional): #hb_tag_t of the requested script
+ * @script_index: (out) (nullable): The index of the requested script
+ * @chosen_script: (out) (nullable): #hb_tag_t of the requested script
  *
  * Selects an OpenType script for @table_tag from the @script_tags array.
  *
@@ -613,7 +613,7 @@ hb_ot_layout_table_select_script (hb_face_t      *face,
  * @face: #hb_face_t to work upon
  * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
  * @start_offset: offset of the first feature tag to retrieve
- * @feature_count: (inout) (optional): Input = the maximum number of feature tags to return;
+ * @feature_count: (inout) (nullable): Input = the maximum number of feature tags to return;
  *                 Output = the actual number of feature tags returned (may be zero)
  * @feature_tags: (out) (array length=feature_count): Array of feature tags found in the table
  *
@@ -683,7 +683,7 @@ hb_ot_layout_table_find_feature (hb_face_t    *face,
  * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
  * @script_index: The index of the requested script tag
  * @start_offset: offset of the first language tag to retrieve
- * @language_count: (inout) (optional): Input = the maximum number of language tags to return;
+ * @language_count: (inout) (nullable): Input = the maximum number of language tags to return;
  *                  Output = the actual number of language tags returned (may be zero)
  * @language_tags: (out) (array length=language_count): Array of language tags found in the table
  *
@@ -911,7 +911,7 @@ hb_ot_layout_language_get_required_feature (hb_face_t    *face,
  * @script_index: The index of the requested script tag
  * @language_index: The index of the requested language tag
  * @start_offset: offset of the first feature tag to retrieve
- * @feature_count: (inout) (optional): Input = the maximum number of feature tags to return;
+ * @feature_count: (inout) (nullable): Input = the maximum number of feature tags to return;
  *                 Output: the actual number of feature tags returned (may be zero)
  * @feature_indexes: (out) (array length=feature_count): The array of feature indexes found for the query
  *
@@ -947,7 +947,7 @@ hb_ot_layout_language_get_feature_indexes (hb_face_t    *face,
  * @script_index: The index of the requested script tag
  * @language_index: The index of the requested language tag
  * @start_offset: offset of the first feature tag to retrieve
- * @feature_count: (inout) (optional): Input = the maximum number of feature tags to return;
+ * @feature_count: (inout) (nullable): Input = the maximum number of feature tags to return;
  *                 Output = the actual number of feature tags returned (may be zero)
  * @feature_tags: (out) (array length=feature_count): The array of #hb_tag_t feature tags found for the query
  *
@@ -1035,7 +1035,7 @@ hb_ot_layout_language_find_feature (hb_face_t    *face,
  * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
  * @feature_index: The index of the requested feature
  * @start_offset: offset of the first lookup to retrieve
- * @lookup_count: (inout) (optional): Input = the maximum number of lookups to return;
+ * @lookup_count: (inout) (nullable): Input = the maximum number of lookups to return;
  *                Output = the actual number of lookups returned (may be zero)
  * @lookup_indexes: (out) (array length=lookup_count): The array of lookup indexes found for the query
  *
@@ -1386,10 +1386,10 @@ hb_ot_layout_collect_lookups (hb_face_t      *face,
  * @face: #hb_face_t to work upon
  * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
  * @lookup_index: The index of the feature lookup to query
- * @glyphs_before: (out): Array of glyphs preceding the substitution range
- * @glyphs_input: (out): Array of input glyphs that would be substituted by the lookup
- * @glyphs_after: (out): Array of glyphs following the substitution range
- * @glyphs_output: (out): Array of glyphs that would be the substituted output of the lookup
+ * @glyphs_before: (out) (nullable): Array of glyphs preceding the substitution range
+ * @glyphs_input: (out) (nullable): Array of input glyphs that would be substituted by the lookup
+ * @glyphs_after: (out) (nullable): Array of glyphs following the substitution range
+ * @glyphs_output: (out) (nullable): Array of glyphs that would be the substituted output of the lookup
  *
  * Fetches a list of all glyphs affected by the specified lookup in the
  * specified face's GSUB table or GPOS table.
@@ -1473,7 +1473,7 @@ hb_ot_layout_table_find_feature_variations (hb_face_t    *face,
  * @feature_index: The index of the feature to query
  * @variations_index: The index of the feature variation to query
  * @start_offset: offset of the first lookup to retrieve
- * @lookup_count: (inout) (optional): Input = the maximum number of lookups to return;
+ * @lookup_count: (inout) (nullable): Input = the maximum number of lookups to return;
  *                Output = the actual number of lookups returned (may be zero)
  * @lookup_indexes: (out) (array length=lookup_count): The array of lookups found for the query
  *
@@ -1777,15 +1777,15 @@ hb_ot_layout_get_size_params (hb_face_t       *face,
  * @face: #hb_face_t to work upon
  * @table_tag: table tag to query, "GSUB" or "GPOS".
  * @feature_index: index of feature to query.
- * @label_id: (out) (optional): The ‘name’ table name ID that specifies a string
- *            for a user-interface label for this feature. (May be NULL.)
- * @tooltip_id: (out) (optional): The ‘name’ table name ID that specifies a string
+ * @label_id: (out) (nullable): The ‘name’ table name ID that specifies a string
+ *            for a user-interface label for this feature.
+ * @tooltip_id: (out) (nullable): The ‘name’ table name ID that specifies a string
  *              that an application can use for tooltip text for this
- *              feature. (May be NULL.)
- * @sample_id: (out) (optional): The ‘name’ table name ID that specifies sample text
- *             that illustrates the effect of this feature. (May be NULL.)
- * @num_named_parameters: (out) (optional):  Number of named parameters. (May be zero.)
- * @first_param_id: (out) (optional): The first ‘name’ table name ID used to specify
+ *              feature.
+ * @sample_id: (out) (nullable): The ‘name’ table name ID that specifies sample text
+ *             that illustrates the effect of this feature.
+ * @num_named_parameters: (out) (nullable):  Number of named parameters.
+ * @first_param_id: (out) (nullable): The first ‘name’ table name ID used to specify
  *                  strings for user-interface labels for the feature
  *                  parameters. (Must be zero if numParameters is zero.)
  *
@@ -1852,7 +1852,7 @@ hb_ot_layout_feature_get_name_ids (hb_face_t       *face,
  * @table_tag: table tag to query, "GSUB" or "GPOS".
  * @feature_index: index of feature to query.
  * @start_offset: offset of the first character to retrieve
- * @char_count: (inout) (optional): Input = the maximum number of characters to return;
+ * @char_count: (inout) (nullable): Input = the maximum number of characters to return;
  *              Output = the actual number of characters returned (may be zero)
  * @characters: (out caller-allocates) (array length=char_count): A buffer pointer.
  *              The Unicode codepoints of the characters for which this feature provides
@@ -2017,7 +2017,7 @@ inline void hb_ot_map_t::apply (const Proxy &proxy,
   unsigned int i = 0;
 
   auto *font_data = font->data.ot.get ();
-  auto *var_store_cache = font_data == HB_SHAPER_DATA_SUCCEEDED ? nullptr : (OT::ItemVariationStore::cache_t *) font_data;
+  auto *var_store_cache = (OT::hb_scalar_cache_t *) font_data;
 
   OT::hb_ot_apply_context_t c (table_index, font, buffer, proxy.accel.get_blob (), var_store_cache);
   c.set_recurse_func (Proxy::Lookup::template dispatch_recurse_func<OT::hb_ot_apply_context_t>);
@@ -2627,7 +2627,7 @@ struct hb_get_glyph_alternates_dispatch_t :
  * @lookup_index: index of the feature lookup to query.
  * @glyph: a glyph id.
  * @start_offset: starting offset.
- * @alternate_count: (inout) (optional): Input = the maximum number of alternate glyphs to return;
+ * @alternate_count: (inout) (nullable): Input = the maximum number of alternate glyphs to return;
  *                   Output = the actual number of alternate glyphs returned (may be zero).
  * @alternate_glyphs: (out caller-allocates) (array length=alternate_count): A glyphs buffer.
  *                    Alternate glyphs associated with the glyph id.

+ 16 - 90
thirdparty/harfbuzz/src/hb-ot-shape.cc

@@ -44,6 +44,7 @@
 #include "hb-ot-face.hh"
 
 #include "hb-set.hh"
+#include "hb-unicode.hh"
 
 #include "hb-aat-layout.hh"
 #include "hb-ot-layout-gdef-table.hh"
@@ -425,25 +426,20 @@ _hb_ot_shaper_face_data_destroy (hb_ot_face_data_t *data)
  */
 
 struct hb_ot_font_data_t {
-  OT::ItemVariationStore::cache_t unused; // Just for alignment
+  OT::hb_scalar_cache_t unused; // Just for alignment
 };
 
 hb_ot_font_data_t *
 _hb_ot_shaper_font_data_create (hb_font_t *font)
 {
-  if (!font->num_coords)
-    return (hb_ot_font_data_t *) HB_SHAPER_DATA_SUCCEEDED;
-
   const OT::ItemVariationStore &var_store = font->face->table.GDEF->table->get_var_store ();
-  auto *cache = (hb_ot_font_data_t *) var_store.create_cache ();
-  return cache ? cache : (hb_ot_font_data_t *) HB_SHAPER_DATA_SUCCEEDED;
+  return (hb_ot_font_data_t *) var_store.create_cache ();
 }
 
 void
 _hb_ot_shaper_font_data_destroy (hb_ot_font_data_t *data)
 {
-  if (data == HB_SHAPER_DATA_SUCCEEDED) return;
-  OT::ItemVariationStore::destroy_cache ((OT::ItemVariationStore::cache_t *) data);
+  OT::ItemVariationStore::destroy_cache ((OT::hb_scalar_cache_t *) data);
 }
 
 
@@ -651,59 +647,6 @@ hb_ensure_native_direction (hb_buffer_t *buffer)
  * Substitute
  */
 
-#ifndef HB_NO_VERTICAL
-static hb_codepoint_t
-hb_vert_char_for (hb_codepoint_t u)
-{
-  switch (u >> 8)
-  {
-    case 0x20: switch (u) {
-      case 0x2013u: return 0xfe32u; // EN DASH
-      case 0x2014u: return 0xfe31u; // EM DASH
-      case 0x2025u: return 0xfe30u; // TWO DOT LEADER
-      case 0x2026u: return 0xfe19u; // HORIZONTAL ELLIPSIS
-    } break;
-    case 0x30: switch (u) {
-      case 0x3001u: return 0xfe11u; // IDEOGRAPHIC COMMA
-      case 0x3002u: return 0xfe12u; // IDEOGRAPHIC FULL STOP
-      case 0x3008u: return 0xfe3fu; // LEFT ANGLE BRACKET
-      case 0x3009u: return 0xfe40u; // RIGHT ANGLE BRACKET
-      case 0x300au: return 0xfe3du; // LEFT DOUBLE ANGLE BRACKET
-      case 0x300bu: return 0xfe3eu; // RIGHT DOUBLE ANGLE BRACKET
-      case 0x300cu: return 0xfe41u; // LEFT CORNER BRACKET
-      case 0x300du: return 0xfe42u; // RIGHT CORNER BRACKET
-      case 0x300eu: return 0xfe43u; // LEFT WHITE CORNER BRACKET
-      case 0x300fu: return 0xfe44u; // RIGHT WHITE CORNER BRACKET
-      case 0x3010u: return 0xfe3bu; // LEFT BLACK LENTICULAR BRACKET
-      case 0x3011u: return 0xfe3cu; // RIGHT BLACK LENTICULAR BRACKET
-      case 0x3014u: return 0xfe39u; // LEFT TORTOISE SHELL BRACKET
-      case 0x3015u: return 0xfe3au; // RIGHT TORTOISE SHELL BRACKET
-      case 0x3016u: return 0xfe17u; // LEFT WHITE LENTICULAR BRACKET
-      case 0x3017u: return 0xfe18u; // RIGHT WHITE LENTICULAR BRACKET
-    } break;
-    case 0xfe: switch (u) {
-      case 0xfe4fu: return 0xfe34u; // WAVY LOW LINE
-    } break;
-    case 0xff: switch (u) {
-      case 0xff01u: return 0xfe15u; // FULLWIDTH EXCLAMATION MARK
-      case 0xff08u: return 0xfe35u; // FULLWIDTH LEFT PARENTHESIS
-      case 0xff09u: return 0xfe36u; // FULLWIDTH RIGHT PARENTHESIS
-      case 0xff0cu: return 0xfe10u; // FULLWIDTH COMMA
-      case 0xff1au: return 0xfe13u; // FULLWIDTH COLON
-      case 0xff1bu: return 0xfe14u; // FULLWIDTH SEMICOLON
-      case 0xff1fu: return 0xfe16u; // FULLWIDTH QUESTION MARK
-      case 0xff3bu: return 0xfe47u; // FULLWIDTH LEFT SQUARE BRACKET
-      case 0xff3du: return 0xfe48u; // FULLWIDTH RIGHT SQUARE BRACKET
-      case 0xff3fu: return 0xfe33u; // FULLWIDTH LOW LINE
-      case 0xff5bu: return 0xfe37u; // FULLWIDTH LEFT CURLY BRACKET
-      case 0xff5du: return 0xfe38u; // FULLWIDTH RIGHT CURLY BRACKET
-    } break;
-  }
-
-  return u;
-}
-#endif
-
 static inline void
 hb_ot_rotate_chars (const hb_ot_shape_context_t *c)
 {
@@ -729,7 +672,7 @@ hb_ot_rotate_chars (const hb_ot_shape_context_t *c)
   if (HB_DIRECTION_IS_VERTICAL (c->target_direction) && !c->plan->has_vert)
   {
     for (unsigned int i = 0; i < count; i++) {
-      hb_codepoint_t codepoint = hb_vert_char_for (info[i].codepoint);
+      hb_codepoint_t codepoint = hb_unicode_funcs_t::vertical_char_for (info[i].codepoint);
       if (unlikely (codepoint != info[i].codepoint && c->font->has_glyph (codepoint)))
 	info[i].codepoint = codepoint;
     }
@@ -1054,23 +997,16 @@ hb_ot_position_default (const hb_ot_shape_context_t *c)
   {
     c->font->get_glyph_h_advances (count, &info[0].codepoint, sizeof(info[0]),
 				   &pos[0].x_advance, sizeof(pos[0]));
-    /* The nil glyph_h_origin() func returns 0, so no need to apply it. */
-    if (c->font->has_glyph_h_origin_func ())
-      for (unsigned int i = 0; i < count; i++)
-	c->font->subtract_glyph_h_origin (info[i].codepoint,
-					  &pos[i].x_offset,
-					  &pos[i].y_offset);
+    // h_origin defaults to zero; only apply it if the font has it.
+    if (c->font->has_glyph_h_origin_func () || c->font->has_glyph_h_origins_func ())
+      c->font->subtract_glyph_h_origins (c->buffer);
   }
   else
   {
     c->font->get_glyph_v_advances (count, &info[0].codepoint, sizeof(info[0]),
 				   &pos[0].y_advance, sizeof(pos[0]));
-    for (unsigned int i = 0; i < count; i++)
-    {
-      c->font->subtract_glyph_v_origin (info[i].codepoint,
-					&pos[i].x_offset,
-					&pos[i].y_offset);
-    }
+    // v_origin defaults to non-zero; apply even if only fallback is there.
+    c->font->subtract_glyph_v_origins (c->buffer);
   }
   if (c->buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_SPACE_FALLBACK)
     _hb_ot_shape_fallback_spaces (c->plan, c->font, c->buffer);
@@ -1079,10 +1015,6 @@ hb_ot_position_default (const hb_ot_shape_context_t *c)
 static inline void
 hb_ot_position_plan (const hb_ot_shape_context_t *c)
 {
-  unsigned int count = c->buffer->len;
-  hb_glyph_info_t *info = c->buffer->info;
-  hb_glyph_position_t *pos = c->buffer->pos;
-
   /* If the font has no GPOS and direction is forward, then when
    * zeroing mark widths, we shift the mark with it, such that the
    * mark is positioned hanging over the previous glyph.  When
@@ -1097,12 +1029,9 @@ hb_ot_position_plan (const hb_ot_shape_context_t *c)
 
   /* We change glyph origin to what GPOS expects (horizontal), apply GPOS, change it back. */
 
-  /* The nil glyph_h_origin() func returns 0, so no need to apply it. */
-  if (c->font->has_glyph_h_origin_func ())
-    for (unsigned int i = 0; i < count; i++)
-      c->font->add_glyph_h_origin (info[i].codepoint,
-				   &pos[i].x_offset,
-				   &pos[i].y_offset);
+  // h_origin defaults to zero; only apply it if the font has it.
+  if (c->font->has_glyph_h_origin_func () || c->font->has_glyph_h_origins_func ())
+    c->font->add_glyph_h_origins (c->buffer);
 
   hb_ot_layout_position_start (c->font, c->buffer);
 
@@ -1139,12 +1068,9 @@ hb_ot_position_plan (const hb_ot_shape_context_t *c)
   hb_ot_zero_width_default_ignorables (c->buffer);
   hb_ot_layout_position_finish_offsets (c->font, c->buffer);
 
-  /* The nil glyph_h_origin() func returns 0, so no need to apply it. */
-  if (c->font->has_glyph_h_origin_func ())
-    for (unsigned int i = 0; i < count; i++)
-      c->font->subtract_glyph_h_origin (info[i].codepoint,
-					&pos[i].x_offset,
-					&pos[i].y_offset);
+  // h_origin defaults to zero; only apply it if the font has it.
+  if (c->font->has_glyph_h_origin_func () || c->font->has_glyph_h_origins_func ())
+    c->font->subtract_glyph_h_origins (c->buffer);
 
   if (c->plan->fallback_mark_positioning)
     _hb_ot_shape_fallback_mark_position (c->plan, c->font, c->buffer,

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

@@ -654,7 +654,7 @@ postprocess_glyphs_arabic (const hb_ot_shape_plan_t *plan,
 
 /* https://www.unicode.org/reports/tr53/ */
 
-static hb_codepoint_t
+static const hb_codepoint_t
 modifier_combining_marks[] =
 {
   0x0654u, /* ARABIC HAMZA ABOVE */

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

@@ -163,7 +163,7 @@ thai_pua_shape (hb_codepoint_t u, thai_action_t action, hb_font_t *font)
 }
 
 
-static enum thai_above_state_t
+static const enum thai_above_state_t
 {     /* Cluster above looks like: */
   T0, /*  ⣤                      */
   T1, /*     ⣼                   */

+ 16 - 20
thirdparty/harfbuzz/src/hb-ot-shaper-use-table.hh

@@ -101,8 +101,9 @@
 
 #ifndef HB_OPTIMIZE_SIZE
 
-static const uint8_t
-hb_use_u8[3345] =
+#include <stdint.h>
+
+static const uint8_t hb_use_u8[3345]=
 {
      16,   50,   51,   51,   51,   52,   51,   83,  118,  131,   57,   58,   59,  195,  211,   62,
      51,   51,   51,   51,   51,   51,   51,   51,   51,   51,   51,   51,   51,   51,   51,   51,
@@ -315,8 +316,7 @@ hb_use_u8[3345] =
       J,   HR,    G,    G,   HM,   HM,   HM,    G,    O, MPre, MPre, MPst,VMAbv, MBlw, VBlw,    O,
    VBlw,
 };
-static const uint16_t
-hb_use_u16[856] =
+static const uint16_t hb_use_u16[856]=
 {
     0,  0,  1,  2,  0,  3,  0,  3,  0,  0,  4,  5,  0,  6,  0,  0,
     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  7,  0,  0,  0,
@@ -374,22 +374,21 @@ hb_use_u16[856] =
   163,163,163,163,163,163,163,130,
 };
 
-static inline unsigned
-hb_use_b4 (const uint8_t* a, unsigned i)
+static inline uint8_t hb_use_b4 (const uint8_t* a, unsigned i)
 {
-  return (a[i>>1]>>((i&1u)<<2))&15u;
+  return (a[i>>1]>>((i&1)<<2))&15;
 }
-static inline uint_fast8_t
-hb_use_get_category (unsigned u)
+static inline uint8_t hb_use_get_category (unsigned u)
 {
-  return u<921600u?hb_use_u8[2953+(((hb_use_u8[625+(((hb_use_u16[((hb_use_u8[113+(((hb_use_b4(hb_use_u8,u>>1>>3>>3>>5))<<5)+((u>>1>>3>>3)&31u))])<<3)+((u>>1>>3)&7u)])<<3)+((u>>1)&7u))])<<1)+((u)&1u))]:O;
+  return u<921600 ? hb_use_u8[2953u+(((hb_use_u8[625u+(((hb_use_u16[((hb_use_u8[113u+(((hb_use_b4(hb_use_u8,u>>1>>3>>3>>5))<<5)+((u>>1>>3>>3)&31))])<<3)+((u>>1>>3)&7)])<<3)+((u>>1)&7))])<<1)+((u)&1))] : O;
 }
 
 
 #else
 
-static const uint8_t
-hb_use_u8[3657] =
+#include <stdint.h>
+
+static const uint8_t hb_use_u8[3657]=
 {
      16,   50,   51,   51,   51,   52,   51,   83,  118,  131,   57,   58,   59,  195,  211,   62,
      51,   51,   51,   51,   51,   51,   51,   51,   51,   51,   51,   51,   51,   51,   51,   51,
@@ -621,8 +620,7 @@ hb_use_u8[3657] =
   VMPst,    G,    G,    J,    J,    J,   SB,   SE,    J,   HR,    G,    G,   HM,   HM,   HM,    G,
       O, MPre, MPre, MPst,VMAbv, MBlw, VBlw,    O, VBlw,
 };
-static const uint16_t
-hb_use_u16[486] =
+static const uint16_t hb_use_u16[486]=
 {
     0,  0,  1,  2,  0,  3,  4,  5,  0,  6,  7,  0,  8,  0,  9, 10,
    11, 12, 10, 13, 14, 10, 10, 15, 16, 17, 18, 19, 20, 21, 22, 23,
@@ -657,15 +655,13 @@ hb_use_u16[486] =
   130,130,163,163,163,130,
 };
 
-static inline unsigned
-hb_use_b4 (const uint8_t* a, unsigned i)
+static inline uint8_t hb_use_b4 (const uint8_t* a, unsigned i)
 {
-  return (a[i>>1]>>((i&1u)<<2))&15u;
+  return (a[i>>1]>>((i&1)<<2))&15;
 }
-static inline uint_fast8_t
-hb_use_get_category (unsigned u)
+static inline uint8_t hb_use_get_category (unsigned u)
 {
-  return u<921600u?hb_use_u8[3265+(((hb_use_u8[937+(((hb_use_u16[((hb_use_u8[369+(((hb_use_u8[113+(((hb_use_b4(hb_use_u8,u>>1>>3>>1>>3>>4))<<4)+((u>>1>>3>>1>>3)&15u))])<<3)+((u>>1>>3>>1)&7u))])<<1)+((u>>1>>3)&1u)])<<3)+((u>>1)&7u))])<<1)+((u)&1u))]:O;
+  return u<921600 ? hb_use_u8[3265u+(((hb_use_u8[937u+(((hb_use_u16[((hb_use_u8[369u+(((hb_use_u8[113u+(((hb_use_b4(hb_use_u8,u>>1>>3>>1>>3>>4))<<4)+((u>>1>>3>>1>>3)&15))])<<3)+((u>>1>>3>>1)&7))])<<1)+((u>>1>>3)&1)])<<3)+((u>>1)&7))])<<1)+((u)&1))] : O;
 }
 
 #endif

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

@@ -6,8 +6,8 @@
  *
  * on files with these headers:
  *
- * <meta name="updated_at" content="2024-12-06 06:35 AM" />
- * File-Date: 2025-01-21
+ * <meta name="updated_at" content="2024-12-06T06:35:00Z" />
+ * File-Date: 2025-03-10
  */
 
 #ifndef HB_OT_TAG_TABLE_HH

+ 94 - 47
thirdparty/harfbuzz/src/hb-ot-var-avar-table.hh

@@ -143,10 +143,13 @@ struct AxisValueMap
 
 struct SegmentMaps : Array16Of<AxisValueMap>
 {
-  int map (int value, unsigned int from_offset = 0, unsigned int to_offset = 1) const
+  float map_float (float value, unsigned int from_offset = 0, unsigned int to_offset = 1) const
   {
-#define fromCoord coords[from_offset].to_int ()
-#define toCoord coords[to_offset].to_int ()
+#define fromCoord coords[from_offset].to_float ()
+#define toCoord coords[to_offset].to_float ()
+
+    const auto *map = arrayZ;
+
     /* The following special-cases are not part of OpenType, which requires
      * that at least -1, 0, and +1 must be mapped. But we include these as
      * part of a better error recovery scheme. */
@@ -155,47 +158,98 @@ struct SegmentMaps : Array16Of<AxisValueMap>
       if (!len)
 	return value;
       else /* len == 1*/
-	return value - arrayZ[0].fromCoord + arrayZ[0].toCoord;
+	return value - map[0].fromCoord + map[0].toCoord;
     }
 
-    if (value <= arrayZ[0].fromCoord)
-      return value - arrayZ[0].fromCoord + arrayZ[0].toCoord;
+    // At least two mappings now.
+
+    /* CoreText is wild...
+     * PingFangUI avar needs all this special-casing...
+     * So we implement an extended version of the spec here,
+     * which is more robust and more likely to be compatible with
+     * the wild. */
+
+    unsigned start = 0;
+    unsigned end = len;
+    if (map[start].fromCoord == -1 && map[start].toCoord == -1 && map[start+1].fromCoord == -1)
+      start++;
+    if (map[end-1].fromCoord == +1 && map[end-1].toCoord == +1 && map[end-2].fromCoord == +1)
+      end--;
+
+    /* Look for exact match first, and do lots of special-casing. */
+    unsigned i;
+    for (i = start; i < end; i++)
+      if (value == map[i].fromCoord)
+	break;
+    if (i < end)
+    {
+      // There's at least one exact match. See if there are more.
+      unsigned j = i;
+      for (; j + 1 < end; j++)
+	if (value != map[j + 1].fromCoord)
+	  break;
+
+      // [i,j] inclusive are all exact matches:
+
+      // If there's only one, return it. This is the only spec-compliant case.
+      if (i == j)
+	return map[i].toCoord;
+      // If there's exactly three, return the middle one.
+      if (i + 2 == j)
+	return map[i + 1].toCoord;
+
+      // Ignore the middle ones. Return the one mapping closer to 0.
+      if (value < 0) return map[j].toCoord;
+      if (value > 0) return map[i].toCoord;
+
+      // Mapping 0? CoreText seems confused. It seems to prefer 0 here...
+      // So we'll just return the smallest one. lol
+      return fabsf (map[i].toCoord) < fabsf (map[j].toCoord) ? map[i].toCoord : map[j].toCoord;
+
+      // Mapping 0? Return one not mapping to 0.
+      if (map[i].toCoord == 0)
+	return map[j].toCoord;
+      else
+	return map[i].toCoord;
+    }
 
-    unsigned int i;
-    unsigned int count = len - 1;
-    for (i = 1; i < count && value > arrayZ[i].fromCoord; i++)
-      ;
+    /* There's at least two and we're not an exact match. Prepare to lerp. */
 
-    if (value >= arrayZ[i].fromCoord)
-      return value - arrayZ[i].fromCoord + arrayZ[i].toCoord;
+    // Find the segment we're in.
+    for (i = start; i < end; i++)
+      if (value < map[i].fromCoord)
+	break;
 
-    if (unlikely (arrayZ[i-1].fromCoord == arrayZ[i].fromCoord))
-      return arrayZ[i-1].toCoord;
+    if (i == 0)
+    {
+      // Value before all segments; Shift.
+      return value - map[0].fromCoord + map[0].toCoord;
+    }
+    if (i == end)
+    {
+      // Value after all segments; Shift.
+      return value - map[end - 1].fromCoord + map[end - 1].toCoord;
+    }
+
+    // Actually interpolate.
+    auto &before = map[i-1];
+    auto &after = map[i];
+    float denom = after.fromCoord - before.fromCoord; // Can't be zero by now.
+    return before.toCoord + ((after.toCoord - before.toCoord) * (value - before.fromCoord)) / denom;
 
-    int denom = arrayZ[i].fromCoord - arrayZ[i-1].fromCoord;
-    return roundf (arrayZ[i-1].toCoord + ((float) (arrayZ[i].toCoord - arrayZ[i-1].toCoord) *
-					  (value - arrayZ[i-1].fromCoord)) / denom);
 #undef toCoord
 #undef fromCoord
   }
 
-  int unmap (int value) const { return map (value, 1, 0); }
+  float unmap_float (float value) const { return map_float (value, 1, 0); }
+
 
+  // TODO Kill this.
   Triple unmap_axis_range (const Triple& axis_range) const
   {
-    F2DOT14 val, unmapped_val;
-
-    val.set_float (axis_range.minimum);
-    unmapped_val.set_int (unmap (val.to_int ()));
-    float unmapped_min = unmapped_val.to_float ();
-
-    val.set_float (axis_range.middle);
-    unmapped_val.set_int (unmap (val.to_int ()));
-    float unmapped_middle = unmapped_val.to_float ();
-
-    val.set_float (axis_range.maximum);
-    unmapped_val.set_int (unmap (val.to_int ()));
-    float unmapped_max = unmapped_val.to_float ();
+    float unmapped_min = unmap_float (axis_range.minimum);
+    float unmapped_middle = unmap_float (axis_range.middle);
+    float unmapped_max = unmap_float (axis_range.maximum);
 
     return Triple{(double) unmapped_min, (double) unmapped_middle, (double) unmapped_max};
   }
@@ -203,6 +257,11 @@ struct SegmentMaps : Array16Of<AxisValueMap>
   bool subset (hb_subset_context_t *c, hb_tag_t axis_tag) const
   {
     TRACE_SUBSET (this);
+
+    /* This function cannot work on avar2 table (and currently doesn't).
+     * We should instead keep the design coords in the shape plan and use
+     * those. unmap_axis_range needs to be killed. */
+
     /* avar mapped normalized axis range*/
     Triple *axis_range;
     if (!c->plan->axes_location.has (axis_tag, &axis_range))
@@ -304,14 +363,14 @@ struct avar
     return_trace (true);
   }
 
-  void map_coords (int *coords, unsigned int coords_length) const
+  void map_coords_16_16 (int *coords, unsigned int coords_length) const
   {
     unsigned int count = hb_min (coords_length, axisCount);
 
     const SegmentMaps *map = &firstAxisSegmentMaps;
     for (unsigned int i = 0; i < count; i++)
     {
-      coords[i] = map->map (coords[i]);
+      coords[i] = roundf (map->map_float (coords[i] / 65536.f) * 65536.f);
       map = &StructAfter<SegmentMaps> (*map);
     }
 
@@ -336,8 +395,8 @@ struct avar
       int v = coords[i];
       uint32_t varidx = varidx_map.map (i);
       float delta = var_store.get_delta (varidx, coords, coords_length, var_store_cache);
-      v += roundf (delta);
-      v = hb_clamp (v, -(1<<14), +(1<<14));
+      v += roundf (delta * 4); // 2.14 -> 16.16
+      v = hb_clamp (v, -(1<<16), +(1<<16));
       out.push (v);
     }
     for (unsigned i = 0; i < coords_length; i++)
@@ -347,18 +406,6 @@ struct avar
 #endif
   }
 
-  void unmap_coords (int *coords, unsigned int coords_length) const
-  {
-    unsigned int count = hb_min (coords_length, axisCount);
-
-    const SegmentMaps *map = &firstAxisSegmentMaps;
-    for (unsigned int i = 0; i < count; i++)
-    {
-      coords[i] = map->unmap (coords[i]);
-      map = &StructAfter<SegmentMaps> (*map);
-    }
-  }
-
   bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);

+ 63 - 52
thirdparty/harfbuzz/src/hb-ot-var-common.hh

@@ -33,7 +33,6 @@
 
 namespace OT {
 
-
 /* https://docs.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#tuplevariationheader */
 struct TupleVariationHeader
 {
@@ -53,7 +52,7 @@ struct TupleVariationHeader
   {
     const F2DOT14 *peak_tuple = nullptr;
     if (has_peak ())
-      peak_tuple = get_peak_tuple (axis_count).arrayZ;
+      peak_tuple = get_peak_tuple (axis_count);
     else
     {
       unsigned int index = get_index ();
@@ -68,8 +67,8 @@ struct TupleVariationHeader
 
     if (has_interm)
     {
-      start_tuple = get_start_tuple (axis_count).arrayZ;
-      end_tuple = get_end_tuple (axis_count).arrayZ;
+      start_tuple = get_start_tuple (axis_count);
+      end_tuple = get_end_tuple (axis_count);
     }
 
     for (unsigned i = 0; i < axis_count; i++)
@@ -98,60 +97,56 @@ struct TupleVariationHeader
     return true;
   }
 
+  HB_ALWAYS_INLINE
   double calculate_scalar (hb_array_t<const int> coords, unsigned int coord_count,
 			   const hb_array_t<const F2DOT14> shared_tuples,
-			   const hb_vector_t<hb_pair_t<int,int>> *shared_tuple_active_idx = nullptr) const
+			   hb_scalar_cache_t *shared_tuple_scalar_cache = nullptr) const
   {
+    unsigned tuple_index = tupleIndex;
+
     const F2DOT14 *peak_tuple;
 
-    unsigned start_idx = 0;
-    unsigned end_idx = coord_count;
-    unsigned step = 1;
+    bool has_interm = tuple_index & TuppleIndex::IntermediateRegion; // Inlined for performance
+    if (unlikely (has_interm))
+      shared_tuple_scalar_cache = nullptr;
 
-    if (has_peak ())
-      peak_tuple = get_peak_tuple (coord_count).arrayZ;
+    if (unlikely (tuple_index & TuppleIndex::EmbeddedPeakTuple)) // Inlined for performance
+    {
+      peak_tuple = get_peak_tuple (coord_count);
+      shared_tuple_scalar_cache = nullptr;
+    }
     else
     {
-      unsigned int index = get_index ();
+      unsigned int index = tuple_index & TuppleIndex::TupleIndexMask; // Inlined for performance
+
+      float scalar;
+      if (shared_tuple_scalar_cache &&
+	  shared_tuple_scalar_cache->get (index, &scalar))
+	return (double) scalar;
+
       if (unlikely ((index + 1) * coord_count > shared_tuples.length))
         return 0.0;
-      peak_tuple = shared_tuples.sub_array (coord_count * index, coord_count).arrayZ;
+      peak_tuple = shared_tuples.arrayZ + (coord_count * index);
 
-      if (shared_tuple_active_idx)
-      {
-	if (unlikely (index >= shared_tuple_active_idx->length))
-	  return 0.0;
-	auto _ = (*shared_tuple_active_idx).arrayZ[index];
-	if (_.second != -1)
-	{
-	  start_idx = _.first;
-	  end_idx = _.second + 1;
-	  step = _.second - _.first;
-	}
-	else if (_.first != -1)
-	{
-	  start_idx = _.first;
-	  end_idx = start_idx + 1;
-	}
-      }
     }
 
     const F2DOT14 *start_tuple = nullptr;
     const F2DOT14 *end_tuple = nullptr;
-    bool has_interm = has_intermediate ();
+
     if (has_interm)
     {
-      start_tuple = get_start_tuple (coord_count).arrayZ;
-      end_tuple = get_end_tuple (coord_count).arrayZ;
+      start_tuple = get_start_tuple (coord_count);
+      end_tuple = get_end_tuple (coord_count);
     }
 
     double scalar = 1.0;
-    for (unsigned int i = start_idx; i < end_idx; i += step)
+    for (unsigned int i = 0; i < coord_count; i++)
     {
       int peak = peak_tuple[i].to_int ();
       if (!peak) continue;
 
       int v = coords[i];
+      if (!v) { scalar = 0.0; break; }
       if (v == peak) continue;
 
       if (has_interm)
@@ -160,16 +155,18 @@ struct TupleVariationHeader
         int end = end_tuple[i].to_int ();
         if (unlikely (start > peak || peak > end ||
                       (start < 0 && end > 0 && peak))) continue;
-        if (v < start || v > end) return 0.0;
+        if (v < start || v > end) { scalar = 0.0; break; }
         if (v < peak)
         { if (peak != start) scalar *= (double) (v - start) / (peak - start); }
         else
         { if (peak != end) scalar *= (double) (end - v) / (end - peak); }
       }
-      else if (!v || v < hb_min (0, peak) || v > hb_max (0, peak)) return 0.0;
+      else if (v < hb_min (0, peak) || v > hb_max (0, peak)) { scalar = 0.0; break; }
       else
         scalar *= (double) v / peak;
     }
+    if (shared_tuple_scalar_cache)
+      shared_tuple_scalar_cache->set (get_index (), scalar);
     return scalar;
   }
 
@@ -194,12 +191,14 @@ struct TupleVariationHeader
 
   hb_array_t<const F2DOT14> get_all_tuples (unsigned axis_count) const
   { return StructAfter<UnsizedArrayOf<F2DOT14>> (tupleIndex).as_array ((has_peak () + has_intermediate () * 2) * axis_count); }
-  hb_array_t<const F2DOT14> get_peak_tuple (unsigned axis_count) const
-  { return get_all_tuples (axis_count).sub_array (0, axis_count); }
-  hb_array_t<const F2DOT14> get_start_tuple (unsigned axis_count) const
-  { return get_all_tuples (axis_count).sub_array (has_peak () * axis_count, axis_count); }
-  hb_array_t<const F2DOT14> get_end_tuple (unsigned axis_count) const
-  { return get_all_tuples (axis_count).sub_array (has_peak () * axis_count + axis_count, axis_count); }
+  const F2DOT14* get_all_tuples_base (unsigned axis_count) const
+  { return StructAfter<UnsizedArrayOf<F2DOT14>> (tupleIndex).arrayZ; }
+  const F2DOT14* get_peak_tuple (unsigned axis_count) const
+  { return get_all_tuples_base (axis_count); }
+  const F2DOT14* get_start_tuple (unsigned axis_count) const
+  { return get_all_tuples_base (axis_count) + has_peak () * axis_count; }
+  const F2DOT14* get_end_tuple (unsigned axis_count) const
+  { return get_all_tuples_base (axis_count) + has_peak () * axis_count + axis_count; }
 
   HBUINT16      varDataSize;    /* The size in bytes of the serialized
                                  * data for this tuple variation table. */
@@ -1330,7 +1329,7 @@ struct TupleVariationData
     {
       var_data_bytes = var_data_bytes_;
       var_data = var_data_bytes_.as<TupleVariationData> ();
-      index = 0;
+      tuples_left = var_data->tupleVarCount.get_count ();
       axis_count = axis_count_;
       current_tuple = &var_data->get_tuple_var_header ();
       data_offset = 0;
@@ -1349,30 +1348,41 @@ struct TupleVariationData
       return true;
     }
 
-    bool is_valid () const
+    bool is_valid ()
     {
-      return (index < var_data->tupleVarCount.get_count ()) &&
-             var_data_bytes.check_range (current_tuple, TupleVariationHeader::min_size) &&
-             var_data_bytes.check_range (current_tuple, hb_max (current_tuple->get_data_size (),
-                                                                current_tuple->get_size (axis_count)));
+      if (unlikely (tuples_left <= 0))
+	return false;
+
+      current_tuple_size = TupleVariationHeader::min_size;
+      if (unlikely (!var_data_bytes.check_range (current_tuple, current_tuple_size)))
+	return false;
+
+      current_tuple_size = current_tuple->get_size (axis_count);
+      if (unlikely (!var_data_bytes.check_range (current_tuple, current_tuple_size)))
+	return false;
+
+      return true;
     }
 
+    HB_ALWAYS_INLINE
     bool move_to_next ()
     {
       data_offset += current_tuple->get_data_size ();
-      current_tuple = &current_tuple->get_next (axis_count);
-      index++;
+      current_tuple = &StructAtOffset<TupleVariationHeader> (current_tuple, current_tuple_size);
+      tuples_left--;
       return is_valid ();
     }
 
+    // TODO: Make it return (sanitized) hb_bytes_t
     const HBUINT8 *get_serialized_data () const
     { return &(table_base+var_data->data) + data_offset; }
 
     private:
+    signed tuples_left;
     const TupleVariationData *var_data;
-    unsigned int index;
     unsigned int axis_count;
     unsigned int data_offset;
+    unsigned int current_tuple_size;
     const void *table_base;
 
     public:
@@ -1449,9 +1459,10 @@ struct TupleVariationData
   static bool decompile_deltas (const HBUINT8 *&p /* IN/OUT */,
 				hb_vector_t<T> &deltas /* IN/OUT */,
 				const HBUINT8 *end,
-				bool consume_all = false)
+				bool consume_all = false,
+				unsigned start = 0)
   {
-    return TupleValues::decompile (p, deltas, end, consume_all);
+    return TupleValues::decompile (p, deltas, end, consume_all, start);
   }
 
   bool has_data () const { return tupleVarCount; }

+ 6 - 23
thirdparty/harfbuzz/src/hb-ot-var-fvar-table.hh

@@ -78,7 +78,7 @@ struct InstanceRecord
         return false;
       if (!axes_location->has (*axis_tag))
         continue;
-      
+
       Triple axis_limit = axes_location->get (*axis_tag);
       if (!axis_coord_pinned_or_within_axis_range (coords, i, axis_limit))
         return false;
@@ -106,7 +106,7 @@ struct InstanceRecord
       {
         if (!axis_coord_pinned_or_within_axis_range (coords, i, *axis_limit))
           return_trace (false);
-        
+
         //skip pinned axis
         if (axis_limit->is_point ())
           continue;
@@ -179,7 +179,7 @@ struct AxisRecord
 
   hb_tag_t get_axis_tag () const { return axisTag; }
 
-  int normalize_axis_value (float v) const
+  float normalize_axis_value (float v) const
   {
     float min_value, default_value, max_value;
     get_coordinates (min_value, default_value, max_value);
@@ -189,23 +189,9 @@ struct AxisRecord
     if (v == default_value)
       return 0;
     else if (v < default_value)
-      v = (v - default_value) / (default_value - min_value);
-    else
-      v = (v - default_value) / (max_value - default_value);
-    return roundf (v * 16384.f);
-  }
-
-  float unnormalize_axis_value (int v) const
-  {
-    float min_value, default_value, max_value;
-    get_coordinates (min_value, default_value, max_value);
-
-    if (v == 0)
-      return default_value;
-    else if (v < 0)
-      return v * (default_value - min_value) / 16384.f + default_value;
+      return (v - default_value) / (default_value - min_value);
     else
-      return v * (max_value - default_value) / 16384.f + default_value;
+      return (v - default_value) / (max_value - default_value);
   }
 
   hb_ot_name_id_t get_name_id () const { return axisNameID; }
@@ -341,12 +327,9 @@ struct fvar
     return axes.lfind (tag, &i) && ((void) axes[i].get_axis_info (i, info), true);
   }
 
-  int normalize_axis_value (unsigned int axis_index, float v) const
+  float normalize_axis_value (unsigned int axis_index, float v) const
   { return get_axes ()[axis_index].normalize_axis_value (v); }
 
-  float unnormalize_axis_value (unsigned int axis_index, int v) const
-  { return get_axes ()[axis_index].unnormalize_axis_value (v); }
-
   unsigned int get_instance_count () const { return instanceCount; }
 
   hb_ot_name_id_t get_instance_subfamily_name_id (unsigned int instance_index) const

+ 19 - 35
thirdparty/harfbuzz/src/hb-ot-var-gvar-table.hh

@@ -582,6 +582,17 @@ struct gvar_GVAR
   public:
   struct accelerator_t
   {
+
+    hb_scalar_cache_t *create_cache () const
+    {
+      return hb_scalar_cache_t::create (table->sharedTupleCount);
+    }
+
+    static void destroy_cache (hb_scalar_cache_t *cache)
+    {
+      hb_scalar_cache_t::destroy (cache);
+    }
+
     bool has_data () const { return table->has_data (); }
 
     accelerator_t (hb_face_t *face)
@@ -589,36 +600,6 @@ struct gvar_GVAR
       table = hb_sanitize_context_t ().reference_table<gvar_GVAR> (face);
       /* If sanitize failed, set glyphCount to 0. */
       glyphCount = table->version.to_int () ? face->get_num_glyphs () : 0;
-
-      /* For shared tuples that only have one or two axes active, shared the index
-       * of that axis as a cache. This will speed up caclulate_scalar() a lot
-       * for fonts with lots of axes and many "monovar" or "duovar" tuples. */
-      hb_array_t<const F2DOT14> shared_tuples = (table+table->sharedTuples).as_array (table->sharedTupleCount * table->axisCount);
-      unsigned count = table->sharedTupleCount;
-      if (unlikely (!shared_tuple_active_idx.resize (count, false))) return;
-      unsigned axis_count = table->axisCount;
-      for (unsigned i = 0; i < count; i++)
-      {
-	hb_array_t<const F2DOT14> tuple = shared_tuples.sub_array (axis_count * i, axis_count);
-	int idx1 = -1, idx2 = -1;
-	for (unsigned j = 0; j < axis_count; j++)
-	{
-	  const F2DOT14 &peak = tuple.arrayZ[j];
-	  if (peak.to_int () != 0)
-	  {
-	    if (idx1 == -1)
-	      idx1 = j;
-	    else if (idx2 == -1)
-	      idx2 = j;
-	    else
-	    {
-	      idx1 = idx2 = -1;
-	      break;
-	    }
-	  }
-	}
-	shared_tuple_active_idx.arrayZ[i] = {idx1, idx2};
-      }
     }
     ~accelerator_t () { table.destroy (); }
 
@@ -655,6 +636,7 @@ struct gvar_GVAR
 				 hb_array_t<const int> coords,
 				 const hb_array_t<contour_point_t> points,
 				 hb_glyf_scratch_t &scratch,
+				 hb_scalar_cache_t *gvar_cache = nullptr,
 				 bool phantom_only = false) const
     {
       if (unlikely (glyph >= glyphCount)) return true;
@@ -690,10 +672,12 @@ struct gvar_GVAR
 
       unsigned count = points.length;
       bool flush = false;
+
       do
       {
 	float scalar = iterator.current_tuple->calculate_scalar (coords, num_coords, shared_tuples,
-								 &shared_tuple_active_idx);
+								 gvar_cache);
+
 	if (scalar == 0.f) continue;
 	const HBUINT8 *p = iterator.get_serialized_data ();
 	unsigned int length = iterator.current_tuple->get_data_size ();
@@ -717,11 +701,12 @@ struct gvar_GVAR
 	const hb_array_t<unsigned int> &indices = has_private_points ? private_indices : shared_indices;
 
 	bool apply_to_all = (indices.length == 0);
-	unsigned int num_deltas = apply_to_all ? points.length : indices.length;
+	unsigned num_deltas = apply_to_all ? points.length : indices.length;
+	unsigned start_deltas = (phantom_only && num_deltas >= 4 ? num_deltas - 4 : 0);
 	if (unlikely (!x_deltas.resize (num_deltas, false))) return false;
-	if (unlikely (!GlyphVariationData::decompile_deltas (p, x_deltas, end))) return false;
+	if (unlikely (!GlyphVariationData::decompile_deltas (p, x_deltas, end, false, start_deltas))) return false;
 	if (unlikely (!y_deltas.resize (num_deltas, false))) return false;
-	if (unlikely (!GlyphVariationData::decompile_deltas (p, y_deltas, end))) return false;
+	if (unlikely (!GlyphVariationData::decompile_deltas (p, y_deltas, end, false, start_deltas))) return false;
 
 	if (!apply_to_all)
 	{
@@ -884,7 +869,6 @@ struct gvar_GVAR
     private:
     hb_blob_ptr_t<gvar_GVAR> table;
     unsigned glyphCount;
-    hb_vector_t<hb_pair_t<int, int>> shared_tuple_active_idx;
   };
 
   protected:

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

@@ -156,7 +156,7 @@ struct index_map_subset_plan_t
       unsigned outer = (*new_varidx) >> 16;
       unsigned bit_count = (outer == 0) ? 1 : hb_bit_storage (outer);
       outer_bit_count = hb_max (bit_count, outer_bit_count);
-      
+
       unsigned inner = (*new_varidx) & 0xFFFF;
       bit_count = (inner == 0) ? 1 : hb_bit_storage (inner);
       inner_bit_count = hb_max (bit_count, inner_bit_count);
@@ -284,6 +284,8 @@ struct HVARVVAR
   static constexpr hb_tag_t HVARTag = HB_OT_TAG_HVAR;
   static constexpr hb_tag_t VVARTag = HB_OT_TAG_VVAR;
 
+  bool has_data () const { return version.major != 0; }
+
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
@@ -382,9 +384,10 @@ struct HVARVVAR
 						hvar_plan.index_map_plans.as_array ()));
   }
 
+  HB_ALWAYS_INLINE
   float get_advance_delta_unscaled (hb_codepoint_t  glyph,
 				    const int *coords, unsigned int coord_count,
-				    ItemVariationStore::cache_t *store_cache = nullptr) const
+				    hb_scalar_cache_t *store_cache = nullptr) const
   {
     uint32_t varidx = (this+advMap).map (glyph);
     return (this+varStore).get_delta (varidx,
@@ -392,16 +395,6 @@ struct HVARVVAR
 				      store_cache);
   }
 
-  bool get_lsb_delta_unscaled (hb_codepoint_t glyph,
-			       const int *coords, unsigned int coord_count,
-			       float *lsb) const
-  {
-    if (!lsbMap) return false;
-    uint32_t varidx = (this+lsbMap).map (glyph);
-    *lsb = (this+varStore).get_delta (varidx, coords, coord_count);
-    return true;
-  }
-
   public:
   FixedVersion<>version;	/* Version of the metrics variation table
 				 * initially set to 0x00010000u */
@@ -454,14 +447,16 @@ struct VVAR : HVARVVAR {
 
   bool subset (hb_subset_context_t *c) const { return HVARVVAR::_subset<VVAR> (c); }
 
-  bool get_vorg_delta_unscaled (hb_codepoint_t glyph,
-				const int *coords, unsigned int coord_count,
-				float *delta) const
+  HB_ALWAYS_INLINE
+  float get_vorg_delta_unscaled (hb_codepoint_t glyph,
+				 const int *coords, unsigned int coord_count,
+				 hb_scalar_cache_t *store_cache = nullptr) const
   {
-    if (!vorgMap) return false;
+    if (!vorgMap) return 0.f;
     uint32_t varidx = (this+vorgMap).map (glyph);
-    *delta = (this+varStore).get_delta (varidx, coords, coord_count);
-    return true;
+    return (this+varStore).get_delta (varidx,
+				      coords, coord_count,
+				      store_cache);
   }
 
   protected:

+ 19 - 7
thirdparty/harfbuzz/src/hb-ot-var.cc

@@ -70,7 +70,7 @@ hb_ot_var_has_data (hb_face_t *face)
  * hb_ot_var_get_axis_count:
  * @face: The #hb_face_t to work on
  *
- * Fetches the number of OpenType variation axes included in the face. 
+ * Fetches the number of OpenType variation axes included in the face.
  *
  * Return value: the number of variation axes defined
  *
@@ -183,7 +183,7 @@ hb_ot_var_find_axis_info (hb_face_t             *face,
  * hb_ot_var_get_named_instance_count:
  * @face: The #hb_face_t to work on
  *
- * Fetches the number of named instances included in the face. 
+ * Fetches the number of named instances included in the face.
  *
  * Return value: the number of named instances defined
  *
@@ -263,7 +263,7 @@ hb_ot_var_named_instance_get_design_coords (hb_face_t    *face,
  * @face: The #hb_face_t to work on
  * @variations: The array of variations to normalize
  * @variations_length: The number of variations to normalize
- * @coords: (out) (array length=coords_length): The array of normalized coordinates 
+ * @coords: (out) (array length=coords_length): The array of normalized coordinates
  * @coords_length: The length of the coordinate array
  *
  * Normalizes all of the coordinates in the given list of variation axes.
@@ -286,10 +286,14 @@ hb_ot_var_normalize_variations (hb_face_t            *face,
     hb_ot_var_axis_info_t info;
     if (hb_ot_var_find_axis_info (face, variations[i].tag, &info) &&
 	info.axis_index < coords_length)
-      coords[info.axis_index] = fvar.normalize_axis_value (info.axis_index, variations[i].value);
+      coords[info.axis_index] = roundf (fvar.normalize_axis_value (info.axis_index, variations[i].value) * 65536.0f);
   }
 
-  face->table.avar->map_coords (coords, coords_length);
+  face->table.avar->map_coords_16_16 (coords, coords_length);
+
+  // Round to 2.14
+  for (unsigned i = 0; i < coords_length; i++)
+    coords[i] = (coords[i] + 2) >> 2;
 }
 
 /**
@@ -309,6 +313,10 @@ hb_ot_var_normalize_variations (hb_face_t            *face,
  * Any additional scaling defined in the face's `avar` table is also
  * applied, as described at https://docs.microsoft.com/en-us/typography/opentype/spec/avar
  *
+ * Note: @coords_length must be the same as the number of axes in the face, as
+ * for example returned by hb_ot_var_get_axis_count().
+ * Otherwise, the behavior is undefined.
+ *
  * Since: 1.4.2
  **/
 void
@@ -319,9 +327,13 @@ hb_ot_var_normalize_coords (hb_face_t    *face,
 {
   const OT::fvar &fvar = *face->table.fvar;
   for (unsigned int i = 0; i < coords_length; i++)
-    normalized_coords[i] = fvar.normalize_axis_value (i, design_coords[i]);
+    normalized_coords[i] = roundf (fvar.normalize_axis_value (i, design_coords[i]) * 65536.0f);
+
+  face->table.avar->map_coords_16_16 (normalized_coords, coords_length);
 
-  face->table.avar->map_coords (normalized_coords, coords_length);
+  // Round to 2.14
+  for (unsigned i = 0; i < coords_length; i++)
+    normalized_coords[i] = (normalized_coords[i] + 2) >> 2;
 }
 
 

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

@@ -61,6 +61,7 @@ struct VORG
 
   bool has_data () const { return version.to_int (); }
 
+  HB_ALWAYS_INLINE
   int get_y_origin (hb_codepoint_t glyph) const
   {
     unsigned int i;

+ 7 - 7
thirdparty/harfbuzz/src/hb-paint-extents.cc

@@ -49,7 +49,7 @@ hb_paint_extents_push_transform (hb_paint_funcs_t *funcs HB_UNUSED,
 {
   hb_paint_extents_context_t *c = (hb_paint_extents_context_t *) paint_data;
 
-  c->push_transform (hb_transform_t {xx, yx, xy, yy, dx, dy});
+  c->push_transform (hb_transform_t<> {xx, yx, xy, yy, dx, dy});
 }
 
 static void
@@ -71,7 +71,7 @@ hb_paint_extents_push_clip_glyph (hb_paint_funcs_t *funcs HB_UNUSED,
 {
   hb_paint_extents_context_t *c = (hb_paint_extents_context_t *) paint_data;
 
-  hb_extents_t extents;
+  hb_extents_t<> extents;
   hb_draw_funcs_t *draw_extent_funcs = hb_draw_extents_get_funcs ();
   hb_font_draw_glyph (font, glyph, draw_extent_funcs, &extents);
   c->push_clip (extents);
@@ -85,7 +85,7 @@ hb_paint_extents_push_clip_rectangle (hb_paint_funcs_t *funcs HB_UNUSED,
 {
   hb_paint_extents_context_t *c = (hb_paint_extents_context_t *) paint_data;
 
-  hb_extents_t extents = {xmin, ymin, xmax, ymax};
+  hb_extents_t<> extents = {xmin, ymin, xmax, ymax};
   c->push_clip (extents);
 }
 
@@ -136,10 +136,10 @@ hb_paint_extents_paint_image (hb_paint_funcs_t *funcs HB_UNUSED,
   if (!glyph_extents)
     return false; // Happens with SVG images.
 
-  hb_extents_t extents = {(float) glyph_extents->x_bearing,
-			  (float) glyph_extents->y_bearing + glyph_extents->height,
-			  (float) glyph_extents->x_bearing + glyph_extents->width,
-			  (float) glyph_extents->y_bearing};
+  hb_extents_t<> extents = {(float) glyph_extents->x_bearing,
+			    (float) glyph_extents->y_bearing + glyph_extents->height,
+			    (float) glyph_extents->x_bearing + glyph_extents->width,
+			    (float) glyph_extents->y_bearing};
   c->push_clip (extents);
   c->paint ();
   c->pop_clip ();

+ 19 - 19
thirdparty/harfbuzz/src/hb-paint-extents.hh

@@ -41,9 +41,9 @@ struct hb_paint_extents_context_t
     clips.clear ();
     groups.clear ();
 
-    transforms.push (hb_transform_t{});
-    clips.push (hb_bounds_t{hb_bounds_t::UNBOUNDED});
-    groups.push (hb_bounds_t{hb_bounds_t::EMPTY});
+    transforms.push (hb_transform_t<>{});
+    clips.push (hb_bounds_t<>{hb_bounds_t<>::UNBOUNDED});
+    groups.push (hb_bounds_t<>{hb_bounds_t<>::EMPTY});
   }
 
   hb_paint_extents_context_t ()
@@ -51,19 +51,19 @@ struct hb_paint_extents_context_t
     clear ();
   }
 
-  hb_extents_t get_extents ()
+  hb_extents_t<> get_extents ()
   {
     return groups.tail().extents;
   }
 
   bool is_bounded ()
   {
-    return groups.tail().status != hb_bounds_t::UNBOUNDED;
+    return groups.tail().status != hb_bounds_t<>::UNBOUNDED;
   }
 
-  void push_transform (const hb_transform_t &trans)
+  void push_transform (const hb_transform_t<> &trans)
   {
-    hb_transform_t t = transforms.tail ();
+    hb_transform_t<> t = transforms.tail ();
     t.multiply (trans);
     transforms.push (t);
   }
@@ -73,13 +73,13 @@ struct hb_paint_extents_context_t
     transforms.pop ();
   }
 
-  void push_clip (hb_extents_t extents)
+  void push_clip (hb_extents_t<> extents)
   {
     /* Transform extents and push a new clip. */
-    const hb_transform_t &t = transforms.tail ();
+    const hb_transform_t<> &t = transforms.tail ();
     t.transform_extents (extents);
 
-    auto bounds = hb_bounds_t {extents};
+    auto bounds = hb_bounds_t<> {extents};
     bounds.intersect (clips.tail ());
 
     clips.push (bounds);
@@ -92,19 +92,19 @@ struct hb_paint_extents_context_t
 
   void push_group ()
   {
-    groups.push (hb_bounds_t {hb_bounds_t::EMPTY});
+    groups.push (hb_bounds_t<> {hb_bounds_t<>::EMPTY});
   }
 
   void pop_group (hb_paint_composite_mode_t mode)
   {
-    const hb_bounds_t src_bounds = groups.pop ();
-    hb_bounds_t &backdrop_bounds = groups.tail ();
+    const hb_bounds_t<> src_bounds = groups.pop ();
+    hb_bounds_t<> &backdrop_bounds = groups.tail ();
 
     // https://learn.microsoft.com/en-us/typography/opentype/spec/colr#format-32-paintcomposite
     switch ((int) mode)
     {
       case HB_PAINT_COMPOSITE_MODE_CLEAR:
-	backdrop_bounds.status = hb_bounds_t::EMPTY;
+	backdrop_bounds.status = hb_bounds_t<>::EMPTY;
 	break;
       case HB_PAINT_COMPOSITE_MODE_SRC:
       case HB_PAINT_COMPOSITE_MODE_SRC_OUT:
@@ -125,16 +125,16 @@ struct hb_paint_extents_context_t
 
   void paint ()
   {
-    const hb_bounds_t &clip = clips.tail ();
-    hb_bounds_t &group = groups.tail ();
+    const hb_bounds_t<> &clip = clips.tail ();
+    hb_bounds_t<> &group = groups.tail ();
 
     group.union_ (clip);
   }
 
   protected:
-  hb_vector_t<hb_transform_t> transforms;
-  hb_vector_t<hb_bounds_t> clips;
-  hb_vector_t<hb_bounds_t> groups;
+  hb_vector_t<hb_transform_t<>> transforms;
+  hb_vector_t<hb_bounds_t<>> clips;
+  hb_vector_t<hb_bounds_t<>> groups;
 };
 
 HB_INTERNAL hb_paint_funcs_t *

+ 42 - 32
thirdparty/harfbuzz/src/hb-paint.hh

@@ -28,6 +28,7 @@
 #include "hb.hh"
 #include "hb-face.hh"
 #include "hb-font.hh"
+#include "hb-geometry.hh"
 
 #define HB_PAINT_FUNCS_IMPLEMENT_CALLBACKS \
   HB_PAINT_FUNC_IMPLEMENT (push_transform) \
@@ -72,7 +73,11 @@ struct hb_paint_funcs_t
                        float xx, float yx,
                        float xy, float yy,
                        float dx, float dy)
-  { func.push_transform (this, paint_data,
+  {
+    // Handle -0.f to avoid -0.f == 0.f in the transform matrix.
+    if (dx == -0.f) dx = 0.f;
+    if (dy == -0.f) dy = 0.f;
+    func.push_transform (this, paint_data,
                          xx, yx, xy, yy, dx, dy,
                          !user_data ? nullptr : user_data->push_transform); }
   void pop_transform (void *paint_data)
@@ -182,54 +187,59 @@ struct hb_paint_funcs_t
 		    0, 0);
   }
 
-  HB_NODISCARD
-  bool push_translate (void *paint_data,
-                       float dx, float dy)
+  void push_transform (void *paint_data, hb_transform_t<float> t)
   {
-    if (!dx && !dy)
-      return false;
+    push_transform (paint_data, t.xx, t.yx, t.xy, t.yy, t.x0, t.y0);
+  }
 
+  void push_translate (void *paint_data,
+                       float dx, float dy)
+  {
     push_transform (paint_data,
-		    1.f, 0.f, 0.f, 1.f, dx, dy);
-    return true;
+		    hb_transform_t<float>::translation (dx, dy));
   }
 
-  HB_NODISCARD
-  bool push_scale (void *paint_data,
+  void push_scale (void *paint_data,
                    float sx, float sy)
   {
-    if (sx == 1.f && sy == 1.f)
-      return false;
-
     push_transform (paint_data,
-		    sx, 0.f, 0.f, sy, 0.f, 0.f);
-    return true;
+		    hb_transform_t<float>::scaling (sx, sy));
+  }
+  void push_scale_around_center (void *paint_data,
+				 float sx, float sy,
+				 float cx, float cy)
+  {
+    push_transform (paint_data,
+		    hb_transform_t<float>::scaling_around_center (sx, sy, cx, cy));
   }
 
-  HB_NODISCARD
-  bool push_rotate (void *paint_data,
+  void push_rotate (void *paint_data,
                     float a)
   {
-    if (!a)
-      return false;
+    push_transform (paint_data,
+		    hb_transform_t<float>::rotation (a * HB_PI));
+  }
 
-    float cc = cosf (a * HB_PI);
-    float ss = sinf (a * HB_PI);
-    push_transform (paint_data, cc, ss, -ss, cc, 0.f, 0.f);
-    return true;
+  void push_rotate_around_center (void *paint_data,
+				  float a,
+				  float cx, float cy)
+  {
+    push_transform (paint_data,
+		    hb_transform_t<float>::rotation_around_center (a * HB_PI, cx, cy));
   }
 
-  HB_NODISCARD
-  bool push_skew (void *paint_data,
+  void push_skew (void *paint_data,
                   float sx, float sy)
   {
-    if (!sx && !sy)
-      return false;
-
-    float x = tanf (-sx * HB_PI);
-    float y = tanf (+sy * HB_PI);
-    push_transform (paint_data, 1.f, y, x, 1.f, 0.f, 0.f);
-    return true;
+    push_transform (paint_data,
+		    hb_transform_t<float>::skewing (-sx * HB_PI, sy * HB_PI));
+  }
+  void push_skew_around_center (void *paint_data,
+				float sx, float sy,
+				float cx, float cy)
+  {
+    push_transform (paint_data,
+		    hb_transform_t<float>::skewing_around_center (-sx * HB_PI, sy * HB_PI, cx, cy));
   }
 };
 DECLARE_NULL_INSTANCE (hb_paint_funcs_t);

+ 7 - 4
thirdparty/harfbuzz/src/hb-repacker.hh

@@ -266,7 +266,7 @@ bool _resolve_shared_overflow(const hb_vector_t<graph::overflow_record_t>& overf
     result = sorted_graph.duplicate(&parents, r.child);
   }
 
-  if (result == (unsigned) -1) return result;
+  if (result == (unsigned) -1) return false;
 
   if (parents.get_population() > 1) {
     // If the duplicated node has more than one parent pre-emptively raise it's priority to the maximum.
@@ -283,7 +283,7 @@ bool _resolve_shared_overflow(const hb_vector_t<graph::overflow_record_t>& overf
     sorted_graph.vertices_[result].give_max_priority();
   }
 
-  return result;
+  return true;
 }
 
 static inline
@@ -302,8 +302,11 @@ bool _process_overflows (const hb_vector_t<graph::overflow_record_t>& overflows,
     {
       // The child object is shared, we may be able to eliminate the overflow
       // by duplicating it.
-      if (!_resolve_shared_overflow(overflows, i, sorted_graph)) continue;
-      return true;
+      if (_resolve_shared_overflow(overflows, i, sorted_graph))
+        return true;
+
+      // Sometimes we can't duplicate a node which looks shared because it's not actually shared
+      // (eg. all links from the same parent) in this case continue on to other resolution options.
     }
 
     if (child.is_leaf () && !priority_bumped_parents.has (r.parent))

+ 18 - 8
thirdparty/harfbuzz/src/hb-sanitize.hh

@@ -120,8 +120,8 @@
 struct hb_sanitize_context_t :
        hb_dispatch_context_t<hb_sanitize_context_t, bool, HB_DEBUG_SANITIZE>
 {
-  hb_sanitize_context_t () :
-	start (nullptr), end (nullptr),
+  hb_sanitize_context_t (const char *start_ = nullptr, const char *end_ = nullptr) :
+	start (start_), end (end_),
 	length (0),
 	max_ops (0), max_subtables (0),
         recursion_depth (0),
@@ -212,14 +212,22 @@ struct hb_sanitize_context_t :
 
   void reset_object ()
   {
-    this->start = this->blob->data;
-    this->end = this->start + this->blob->length;
+    if (this->blob)
+    {
+      this->start = this->blob->data;
+      this->end = this->start + this->blob->length;
+    }
     this->length = this->end - this->start;
     assert (this->start <= this->end); /* Must not overflow. */
   }
 
-  void start_processing ()
+  void start_processing (const char *start_ = nullptr, const char *end_ = nullptr)
   {
+    if (start_)
+    {
+      this->start = start_;
+      this->end = end_;
+    }
     reset_object ();
     unsigned m;
     if (unlikely (hb_unsigned_mul_overflows (this->end - this->start, HB_SANITIZE_MAX_OPS_FACTOR, &m)))
@@ -463,9 +471,11 @@ struct hb_sanitize_context_t :
     }
     else
     {
-      if (edit_count && !writable) {
-	start = hb_blob_get_data_writable (blob, nullptr);
-	end = start + blob->length;
+      if (edit_count && !writable)
+      {
+        unsigned length;
+	start = hb_blob_get_data_writable (blob, &length);
+	end = start + length;
 
 	if (start)
 	{

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

@@ -794,7 +794,8 @@ struct hb_serialize_context_t
   template <typename T, unsigned Size = sizeof (T)>
   void assign_offset (const object_t* parent, const object_t::link_t &link, unsigned offset)
   {
-    auto &off = * ((BEInt<T, Size> *) (parent->head + link.position));
+    // XXX We should stop assuming big-endian!
+    auto &off = * ((HBInt<true, T, Size> *) (parent->head + link.position));
     assert (0 == off);
     check_assign (off, offset, HB_SERIALIZE_ERROR_OFFSET_OVERFLOW);
   }

+ 2 - 2
thirdparty/harfbuzz/src/hb-shaper-list.hh

@@ -57,8 +57,8 @@ HB_SHAPER_IMPLEMENT (directwrite)
 HB_SHAPER_IMPLEMENT (coretext)
 #endif
 
-#ifdef HAVE_HARFRUZZ
-HB_SHAPER_IMPLEMENT (harfruzz)
+#ifdef HAVE_HARFRUST
+HB_SHAPER_IMPLEMENT (harfrust)
 #endif
 
 #ifndef HB_NO_FALLBACK_SHAPE

+ 0 - 23
thirdparty/harfbuzz/src/hb-static.cc

@@ -113,27 +113,4 @@ hb_face_t::load_upem () const
   return ret;
 }
 
-
-#ifndef HB_NO_VAR
-bool
-_glyf_get_leading_bearing_with_var_unscaled (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical,
-					     int *lsb)
-{
-  return font->face->table.glyf->get_leading_bearing_with_var_unscaled (font, glyph, is_vertical, lsb);
-}
-
-unsigned
-_glyf_get_advance_with_var_unscaled (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical)
-{
-  return font->face->table.glyf->get_advance_with_var_unscaled (font, glyph, is_vertical);
-}
-#endif
-
-bool
-_glyf_get_leading_bearing_without_var_unscaled (hb_face_t *face, hb_codepoint_t gid, bool is_vertical, int *lsb)
-{
-  return face->table.glyf->get_leading_bearing_without_var_unscaled (gid, is_vertical, lsb);
-}
-
-
 #endif

+ 62 - 39
thirdparty/harfbuzz/src/hb-subset-plan-var.cc

@@ -42,30 +42,30 @@
                                hb_vector_t<hb_inc_bimap_t> &inner_maps /* OUT */)
  {
    if (varidx_set.is_empty () || subtable_count == 0) return;
- 
+
    if (unlikely (!inner_maps.resize (subtable_count))) return;
    for (unsigned idx : varidx_set)
    {
      uint16_t major = idx >> 16;
      uint16_t minor = idx & 0xFFFF;
- 
+
      if (major >= subtable_count)
        continue;
      inner_maps[major].add (minor);
    }
  }
- 
+
  static inline hb_font_t*
  _get_hb_font_with_variations (const hb_subset_plan_t *plan)
  {
    hb_font_t *font = hb_font_create (plan->source);
- 
+
    hb_vector_t<hb_variation_t> vars;
    if (!vars.alloc (plan->user_axes_location.get_population ())) {
      hb_font_destroy (font);
      return nullptr;
    }
- 
+
    for (auto _ : plan->user_axes_location)
    {
      hb_variation_t var;
@@ -73,11 +73,11 @@
      var.value = _.second.middle;
      vars.push (var);
    }
- 
+
    hb_font_set_variations (font, vars.arrayZ, plan->user_axes_location.get_population ());
    return font;
  }
- 
+
  template<typename ItemVarStore>
  void
  remap_variation_indices (const ItemVarStore &var_store,
@@ -90,7 +90,7 @@
    if (&var_store == &Null (OT::ItemVariationStore)) return;
    unsigned subtable_count = var_store.get_sub_table_count ();
    auto *store_cache = var_store.create_cache ();
- 
+
    unsigned new_major = 0, new_minor = 0;
    unsigned last_major = (variation_indices.get_min ()) >> 16;
    for (unsigned idx : variation_indices)
@@ -99,13 +99,13 @@
      if (calculate_delta)
        delta = roundf (var_store.get_delta (idx, normalized_coords.arrayZ,
                                             normalized_coords.length, store_cache));
- 
+
      if (no_variations)
      {
        variation_idx_delta_map.set (idx, hb_pair_t<unsigned, int> (HB_OT_LAYOUT_NO_VARIATIONS_INDEX, delta));
        continue;
      }
- 
+
      uint16_t major = idx >> 16;
      if (major >= subtable_count) break;
      if (major != last_major)
@@ -113,7 +113,7 @@
        new_minor = 0;
        ++new_major;
      }
- 
+
      unsigned new_idx = (new_major << 16) + new_minor;
      variation_idx_delta_map.set (idx, hb_pair_t<unsigned, int> (new_idx, delta));
      ++new_minor;
@@ -121,7 +121,7 @@
    }
    var_store.destroy_cache (store_cache);
  }
- 
+
  template
  void
  remap_variation_indices<OT::ItemVariationStore> (const OT::ItemVariationStore &var_store,
@@ -130,7 +130,7 @@
                           bool calculate_delta, /* not pinned at default */
                           bool no_variations, /* all axes pinned */
                           hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> &variation_idx_delta_map /* OUT */);
- 
+
  #ifndef HB_NO_BASE
  void
  collect_base_variation_indices (hb_subset_plan_t* plan)
@@ -141,23 +141,23 @@
      base.destroy ();
      return;
    }
- 
+
    hb_set_t varidx_set;
    base->collect_variation_indices (plan, varidx_set);
    const OT::ItemVariationStore &var_store = base->get_var_store ();
    unsigned subtable_count = var_store.get_sub_table_count ();
- 
- 
+
+
    remap_variation_indices (var_store, varidx_set,
                              plan->normalized_coords,
                              !plan->pinned_at_default,
                              plan->all_axes_pinned,
                              plan->base_variation_idx_map);
    generate_varstore_inner_maps (varidx_set, subtable_count, plan->base_varstore_inner_maps);
- 
+
    base.destroy ();
  }
- 
+
  #endif
 
 void
@@ -199,24 +199,44 @@ normalize_axes_location (hb_face_t *face, hb_subset_plan_t *plan)
     {
       plan->axes_triple_distances.set (axis_tag, axis.get_triple_distances ());
 
-      int normalized_min = axis.normalize_axis_value (axis_range->minimum);
-      int normalized_default = axis.normalize_axis_value (axis_range->middle);
-      int normalized_max = axis.normalize_axis_value (axis_range->maximum);
+      float normalized_min = axis.normalize_axis_value (axis_range->minimum);
+      float normalized_default = axis.normalize_axis_value (axis_range->middle);
+      float normalized_max = axis.normalize_axis_value (axis_range->maximum);
+
+      // TODO(behdad): Spec says axis normalization should be done in 16.16;
+      // We used to do it in 2.14, but that's not correct.  I fixed this in
+      // the fvar/avar code, but keeping 2.14 here for now to keep tests
+      // happy. We might need to adjust fonttools as well.
+      // I'm only fairly confident in the above statement. Anyway,
+      // we should look deeper into this, and also update fonttools if
+      // needed.
+
+      // Round to 2.14
+      normalized_min = roundf (normalized_min * 16384.f) / 16384.f;
+      normalized_default = roundf (normalized_default * 16384.f) / 16384.f;
+      normalized_max = roundf (normalized_max * 16384.f) / 16384.f;
 
       if (has_avar && old_axis_idx < avar_axis_count)
       {
-        normalized_min = seg_maps->map (normalized_min);
-        normalized_default = seg_maps->map (normalized_default);
-        normalized_max = seg_maps->map (normalized_max);
+	normalized_min = seg_maps->map_float (normalized_min);
+	normalized_default = seg_maps->map_float (normalized_default);
+	normalized_max = seg_maps->map_float (normalized_max);
+
+	// Round to 2.14
+	normalized_min = roundf (normalized_min * 16384.f) / 16384.f;
+	normalized_default = roundf (normalized_default * 16384.f) / 16384.f;
+	normalized_max = roundf (normalized_max * 16384.f) / 16384.f;
       }
-      plan->axes_location.set (axis_tag, Triple (static_cast<double> (normalized_min / 16384.0),
-                                                 static_cast<double> (normalized_default / 16384.0),
-                                                 static_cast<double> (normalized_max / 16384.0)));
+      plan->axes_location.set (axis_tag, Triple ((double) normalized_min,
+                                                 (double) normalized_default,
+                                                 (double) normalized_max));
 
-      if (normalized_default != 0)
+      if (normalized_default == -0.f)
+        normalized_default = 0.f; // Normalize -0 to 0
+      if (normalized_default != 0.f)
         plan->pinned_at_default = false;
 
-      plan->normalized_coords[old_axis_idx] = normalized_default;
+      plan->normalized_coords[old_axis_idx] = roundf (normalized_default * 16384.f);
     }
 
     old_axis_idx++;
@@ -243,12 +263,12 @@ update_instance_metrics_map_from_cff2 (hb_subset_plan_t *plan)
 
   hb_glyph_extents_t extents = {0x7FFF, -0x7FFF};
   OT::hmtx_accelerator_t _hmtx (plan->source);
-  OT::ItemVariationStore::cache_t *hvar_store_cache = nullptr;
+  OT::hb_scalar_cache_t *hvar_store_cache = nullptr;
   if (_hmtx.has_data () && _hmtx.var_table.get_length ())
     hvar_store_cache = _hmtx.var_table->get_var_store ().create_cache ();
 
   OT::vmtx_accelerator_t _vmtx (plan->source);
-  OT::ItemVariationStore::cache_t *vvar_store_cache = nullptr;
+  OT::hb_scalar_cache_t *vvar_store_cache = nullptr;
   if (_vmtx.has_data () && _vmtx.var_table.get_length ())
     vvar_store_cache = _vmtx.var_table->get_var_store ().create_cache ();
 
@@ -279,8 +299,7 @@ update_instance_metrics_map_from_cff2 (hb_subset_plan_t *plan)
       int lsb = extents.x_bearing;
       if (!has_bounds_info)
       {
-        if (!_hmtx.get_leading_bearing_without_var_unscaled (old_gid, &lsb))
-          continue;
+        _hmtx.get_leading_bearing_without_var_unscaled (old_gid, &lsb);
       }
       plan->hmtx_map.set (new_gid, hb_pair ((unsigned) hori_aw, lsb));
       plan->bounds_width_vec[new_gid] = extents.width;
@@ -292,13 +311,17 @@ update_instance_metrics_map_from_cff2 (hb_subset_plan_t *plan)
       if (_vmtx.var_table.get_length ())
         vert_aw += (int) roundf (_vmtx.var_table->get_advance_delta_unscaled (old_gid, font->coords, font->num_coords,
                                                                               vvar_store_cache));
-
-      int tsb = extents.y_bearing;
-      if (!has_bounds_info)
+      hb_position_t vorg_x = 0;
+      hb_position_t vorg_y = 0;
+      int tsb = 0;
+      if (has_bounds_info &&
+           hb_font_get_glyph_v_origin (font, old_gid, &vorg_x, &vorg_y))
       {
-        if (!_vmtx.get_leading_bearing_without_var_unscaled (old_gid, &tsb))
-          continue;
+        tsb = vorg_y - extents.y_bearing;
+      } else {
+        _vmtx.get_leading_bearing_without_var_unscaled (old_gid, &tsb);
       }
+
       plan->vmtx_map.set (new_gid, hb_pair ((unsigned) vert_aw, tsb));
       plan->bounds_height_vec[new_gid] = extents.height;
     }
@@ -385,4 +408,4 @@ remap_colrv1_delta_set_index_indices<OT::DeltaSetIndexMap> (const OT::DeltaSetIn
                                       hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> &variation_idx_delta_map, /* IN/OUT */
                                       hb_map_t &new_deltaset_idx_varidx_map /* OUT */);
 
- #endif
+ #endif

+ 2 - 2
thirdparty/harfbuzz/src/hb-subset-plan.cc

@@ -248,7 +248,7 @@ _populate_unicodes_to_retain (const hb_set_t *unicodes_in,
     !(plan->flags & HB_SUBSET_FLAGS_NO_BIDI_CLOSURE));
 
   OT::cmap::accelerator_t cmap (plan->source);
-  unsigned size_threshold = plan->source->get_num_glyphs ();  
+  unsigned size_threshold = plan->source->get_num_glyphs ();
 
   if (glyphs->is_empty () && unicodes.get_population () < size_threshold)
   {
@@ -376,7 +376,7 @@ _populate_unicodes_to_retain (const hb_set_t *unicodes_in,
   // so record those first.
   plan->os2_info.min_cmap_codepoint = plan->unicodes.get_min();
   plan->os2_info.max_cmap_codepoint = plan->unicodes.get_max();
-  
+
   hb_set_t variation_selectors_to_retain;
   cmap.collect_variation_selectors(&variation_selectors_to_retain);
   + variation_selectors_to_retain.iter()

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 652 - 973
thirdparty/harfbuzz/src/hb-ucd-table.hh


+ 9 - 11
thirdparty/harfbuzz/src/hb-unicode-emoji-table.hh

@@ -23,8 +23,9 @@
 
 #include "hb-unicode.hh"
 
-static const uint8_t
-_hb_emoji_u8[464] =
+#include <stdint.h>
+
+static const uint8_t _hb_emoji_u8[464]=
 {
    16, 17, 17, 17, 50, 20, 21, 17, 17, 17, 17, 17, 17, 17, 17, 17,
    17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
@@ -57,20 +58,17 @@ _hb_emoji_u8[464] =
     0,192,255,255,  0,240,255,255,255,255,255,247,191,255,255,255,
 };
 
-static inline unsigned
-_hb_emoji_b4 (const uint8_t* a, unsigned i)
+static inline uint8_t _hb_emoji_b4 (const uint8_t* a, unsigned i)
 {
-  return (a[i>>1]>>((i&1u)<<2))&15u;
+  return (a[i>>1]>>((i&1)<<2))&15;
 }
-static inline unsigned
-_hb_emoji_b1 (const uint8_t* a, unsigned i)
+static inline uint8_t _hb_emoji_b1 (const uint8_t* a, unsigned i)
 {
-  return (a[i>>3]>>((i&7u)<<0))&1u;
+  return (a[i>>3]>>((i&7)<<0))&1;
 }
-static inline uint_fast8_t
-_hb_emoji_is_Extended_Pictographic (unsigned u)
+static inline uint8_t _hb_emoji_is_Extended_Pictographic (unsigned u)
 {
-  return u<131070u?_hb_emoji_b1(264+_hb_emoji_u8,((_hb_emoji_u8[144+(((_hb_emoji_u8[64+(((_hb_emoji_b4(_hb_emoji_u8,u>>5>>2>>3))<<3)+((u>>5>>2)&7u))])<<2)+((u>>5)&3u))])<<5)+((u)&31u)):0;
+  return u<131070 ? _hb_emoji_b1(_hb_emoji_u8+264u,((_hb_emoji_u8[144u+(((_hb_emoji_u8[64u+(((_hb_emoji_b4(_hb_emoji_u8,u>>5>>2>>3))<<3)+((u>>5>>2)&7))])<<2)+((u>>5)&3))])<<5)+((u)&31)) : 0;
 }
 
 

+ 51 - 0
thirdparty/harfbuzz/src/hb-unicode.hh

@@ -241,6 +241,57 @@ HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS_SIMPLE
     }
   }
 
+  static hb_codepoint_t
+  vertical_char_for (hb_codepoint_t u)
+  {
+    switch (u >> 8)
+    {
+      case 0x20: switch (u) {
+	case 0x2013u: return 0xfe32u; // EN DASH
+	case 0x2014u: return 0xfe31u; // EM DASH
+	case 0x2025u: return 0xfe30u; // TWO DOT LEADER
+	case 0x2026u: return 0xfe19u; // HORIZONTAL ELLIPSIS
+      } break;
+      case 0x30: switch (u) {
+	case 0x3001u: return 0xfe11u; // IDEOGRAPHIC COMMA
+	case 0x3002u: return 0xfe12u; // IDEOGRAPHIC FULL STOP
+	case 0x3008u: return 0xfe3fu; // LEFT ANGLE BRACKET
+	case 0x3009u: return 0xfe40u; // RIGHT ANGLE BRACKET
+	case 0x300au: return 0xfe3du; // LEFT DOUBLE ANGLE BRACKET
+	case 0x300bu: return 0xfe3eu; // RIGHT DOUBLE ANGLE BRACKET
+	case 0x300cu: return 0xfe41u; // LEFT CORNER BRACKET
+	case 0x300du: return 0xfe42u; // RIGHT CORNER BRACKET
+	case 0x300eu: return 0xfe43u; // LEFT WHITE CORNER BRACKET
+	case 0x300fu: return 0xfe44u; // RIGHT WHITE CORNER BRACKET
+	case 0x3010u: return 0xfe3bu; // LEFT BLACK LENTICULAR BRACKET
+	case 0x3011u: return 0xfe3cu; // RIGHT BLACK LENTICULAR BRACKET
+	case 0x3014u: return 0xfe39u; // LEFT TORTOISE SHELL BRACKET
+	case 0x3015u: return 0xfe3au; // RIGHT TORTOISE SHELL BRACKET
+	case 0x3016u: return 0xfe17u; // LEFT WHITE LENTICULAR BRACKET
+	case 0x3017u: return 0xfe18u; // RIGHT WHITE LENTICULAR BRACKET
+      } break;
+      case 0xfe: switch (u) {
+	case 0xfe4fu: return 0xfe34u; // WAVY LOW LINE
+      } break;
+      case 0xff: switch (u) {
+	case 0xff01u: return 0xfe15u; // FULLWIDTH EXCLAMATION MARK
+	case 0xff08u: return 0xfe35u; // FULLWIDTH LEFT PARENTHESIS
+	case 0xff09u: return 0xfe36u; // FULLWIDTH RIGHT PARENTHESIS
+	case 0xff0cu: return 0xfe10u; // FULLWIDTH COMMA
+	case 0xff1au: return 0xfe13u; // FULLWIDTH COLON
+	case 0xff1bu: return 0xfe14u; // FULLWIDTH SEMICOLON
+	case 0xff1fu: return 0xfe16u; // FULLWIDTH QUESTION MARK
+	case 0xff3bu: return 0xfe47u; // FULLWIDTH LEFT SQUARE BRACKET
+	case 0xff3du: return 0xfe48u; // FULLWIDTH RIGHT SQUARE BRACKET
+	case 0xff3fu: return 0xfe33u; // FULLWIDTH LOW LINE
+	case 0xff5bu: return 0xfe37u; // FULLWIDTH LEFT CURLY BRACKET
+	case 0xff5du: return 0xfe38u; // FULLWIDTH RIGHT CURLY BRACKET
+      } break;
+    }
+
+    return u;
+  }
+
   struct {
 #define HB_UNICODE_FUNC_IMPLEMENT(name) hb_unicode_##name##_func_t name;
     HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS

+ 3 - 3
thirdparty/harfbuzz/src/hb-version.h

@@ -47,20 +47,20 @@ HB_BEGIN_DECLS
  *
  * The minor component of the library version available at compile-time.
  */
-#define HB_VERSION_MINOR 2
+#define HB_VERSION_MINOR 3
 /**
  * HB_VERSION_MICRO:
  *
  * The micro component of the library version available at compile-time.
  */
-#define HB_VERSION_MICRO 1
+#define HB_VERSION_MICRO 2
 
 /**
  * HB_VERSION_STRING:
  *
  * A string literal containing the library version available at compile-time.
  */
-#define HB_VERSION_STRING "11.2.1"
+#define HB_VERSION_STRING "11.3.2"
 
 /**
  * HB_VERSION_ATLEAST:

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

@@ -314,6 +314,10 @@
 #endif
 #endif
 
+#ifndef HB_HOT
+#define HB_HOT __attribute__((hot))
+#endif
+
 /*
  * Borrowed from https://bugzilla.mozilla.org/show_bug.cgi?id=1215411
  * HB_FALLTHROUGH is an annotation to suppress compiler warnings about switch

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů