فهرست منبع

Update HarfBuzz to 7.1.0

Pedro J. Estébanez 2 سال پیش
والد
کامیت
abc13dbd0b
100فایلهای تغییر یافته به همراه7066 افزوده شده و 2097 حذف شده
  1. 13 9
      COPYRIGHT.txt
  2. 13 6
      modules/text_server_adv/SCsub
  3. 14 6
      modules/text_server_adv/gdextension_build/SConstruct
  4. 4 2
      thirdparty/README.md
  5. 12 8
      thirdparty/harfbuzz/COPYING
  6. 55 22
      thirdparty/harfbuzz/src/OT/Color/CBDT/CBDT.hh
  7. 607 45
      thirdparty/harfbuzz/src/OT/Color/COLR/COLR.hh
  8. 5 6
      thirdparty/harfbuzz/src/OT/Color/COLR/colrv1-closure.hh
  9. 7 7
      thirdparty/harfbuzz/src/OT/Color/CPAL/CPAL.hh
  10. 49 20
      thirdparty/harfbuzz/src/OT/Color/sbix/sbix.hh
  11. 29 4
      thirdparty/harfbuzz/src/OT/Color/svg/svg.hh
  12. 1 0
      thirdparty/harfbuzz/src/OT/Layout/Common/Coverage.hh
  13. 918 0
      thirdparty/harfbuzz/src/OT/Layout/GDEF/GDEF.hh
  14. 6 6
      thirdparty/harfbuzz/src/OT/Layout/GPOS/AnchorFormat3.hh
  15. 1 1
      thirdparty/harfbuzz/src/OT/Layout/GPOS/CursivePos.hh
  16. 2 2
      thirdparty/harfbuzz/src/OT/Layout/GPOS/CursivePosFormat1.hh
  17. 2 2
      thirdparty/harfbuzz/src/OT/Layout/GPOS/MarkArray.hh
  18. 1 1
      thirdparty/harfbuzz/src/OT/Layout/GPOS/MarkBasePos.hh
  19. 54 29
      thirdparty/harfbuzz/src/OT/Layout/GPOS/MarkBasePosFormat1.hh
  20. 1 1
      thirdparty/harfbuzz/src/OT/Layout/GPOS/MarkLigPos.hh
  21. 28 11
      thirdparty/harfbuzz/src/OT/Layout/GPOS/MarkLigPosFormat1.hh
  22. 1 1
      thirdparty/harfbuzz/src/OT/Layout/GPOS/MarkMarkPos.hh
  23. 1 1
      thirdparty/harfbuzz/src/OT/Layout/GPOS/PairPos.hh
  24. 2 4
      thirdparty/harfbuzz/src/OT/Layout/GPOS/PairPosFormat1.hh
  25. 6 6
      thirdparty/harfbuzz/src/OT/Layout/GPOS/PairPosFormat2.hh
  26. 21 17
      thirdparty/harfbuzz/src/OT/Layout/GPOS/PairSet.hh
  27. 1 1
      thirdparty/harfbuzz/src/OT/Layout/GPOS/SinglePos.hh
  28. 11 3
      thirdparty/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat1.hh
  29. 3 3
      thirdparty/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat2.hh
  30. 1 1
      thirdparty/harfbuzz/src/OT/Layout/GPOS/ValueFormat.hh
  31. 3 3
      thirdparty/harfbuzz/src/OT/Layout/GSUB/AlternateSet.hh
  32. 1 1
      thirdparty/harfbuzz/src/OT/Layout/GSUB/AlternateSubst.hh
  33. 4 4
      thirdparty/harfbuzz/src/OT/Layout/GSUB/Ligature.hh
  34. 1 1
      thirdparty/harfbuzz/src/OT/Layout/GSUB/LigatureSubst.hh
  35. 1 1
      thirdparty/harfbuzz/src/OT/Layout/GSUB/MultipleSubst.hh
  36. 1 1
      thirdparty/harfbuzz/src/OT/Layout/GSUB/ReverseChainSingleSubst.hh
  37. 2 2
      thirdparty/harfbuzz/src/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh
  38. 6 6
      thirdparty/harfbuzz/src/OT/Layout/GSUB/Sequence.hh
  39. 1 1
      thirdparty/harfbuzz/src/OT/Layout/GSUB/SingleSubst.hh
  40. 12 4
      thirdparty/harfbuzz/src/OT/Layout/GSUB/SingleSubstFormat1.hh
  41. 3 3
      thirdparty/harfbuzz/src/OT/Layout/GSUB/SingleSubstFormat2.hh
  42. 116 42
      thirdparty/harfbuzz/src/OT/glyf/Glyph.hh
  43. 6 4
      thirdparty/harfbuzz/src/OT/glyf/GlyphHeader.hh
  44. 5 5
      thirdparty/harfbuzz/src/OT/glyf/SimpleGlyph.hh
  45. 2 10
      thirdparty/harfbuzz/src/OT/glyf/SubsetGlyph.hh
  46. 16 15
      thirdparty/harfbuzz/src/OT/glyf/VarCompositeGlyph.hh
  47. 16 2
      thirdparty/harfbuzz/src/OT/glyf/glyf-helpers.hh
  48. 70 44
      thirdparty/harfbuzz/src/OT/glyf/glyf.hh
  49. 77 19
      thirdparty/harfbuzz/src/OT/glyf/path-builder.hh
  50. 589 0
      thirdparty/harfbuzz/src/OT/name/name.hh
  51. 12 5
      thirdparty/harfbuzz/src/graph/graph.hh
  52. 3 3
      thirdparty/harfbuzz/src/graph/serialize.hh
  53. 0 119
      thirdparty/harfbuzz/src/graph/test-classdef-graph.cc
  54. 68 37
      thirdparty/harfbuzz/src/hb-aat-layout-common.hh
  55. 6 4
      thirdparty/harfbuzz/src/hb-aat-layout-kerx-table.hh
  56. 43 14
      thirdparty/harfbuzz/src/hb-aat-layout-morx-table.hh
  57. 11 3
      thirdparty/harfbuzz/src/hb-aat-layout.cc
  58. 3 1
      thirdparty/harfbuzz/src/hb-aat-layout.hh
  59. 100 30
      thirdparty/harfbuzz/src/hb-aat-map.cc
  60. 35 10
      thirdparty/harfbuzz/src/hb-aat-map.hh
  61. 80 8
      thirdparty/harfbuzz/src/hb-algs.hh
  62. 3 0
      thirdparty/harfbuzz/src/hb-array.hh
  63. 33 8
      thirdparty/harfbuzz/src/hb-atomic.hh
  64. 18 8
      thirdparty/harfbuzz/src/hb-bit-page.hh
  65. 5 0
      thirdparty/harfbuzz/src/hb-bit-set-invertible.hh
  66. 11 7
      thirdparty/harfbuzz/src/hb-bit-set.hh
  67. 3 3
      thirdparty/harfbuzz/src/hb-blob.cc
  68. 1 1
      thirdparty/harfbuzz/src/hb-blob.h
  69. 185 187
      thirdparty/harfbuzz/src/hb-buffer-deserialize-json.hh
  70. 692 0
      thirdparty/harfbuzz/src/hb-buffer-deserialize-text-glyphs.hh
  71. 332 0
      thirdparty/harfbuzz/src/hb-buffer-deserialize-text-unicode.hh
  72. 0 917
      thirdparty/harfbuzz/src/hb-buffer-deserialize-text.hh
  73. 12 9
      thirdparty/harfbuzz/src/hb-buffer-serialize.cc
  74. 2 2
      thirdparty/harfbuzz/src/hb-buffer-verify.cc
  75. 33 5
      thirdparty/harfbuzz/src/hb-buffer.cc
  76. 1 1
      thirdparty/harfbuzz/src/hb-buffer.h
  77. 44 26
      thirdparty/harfbuzz/src/hb-buffer.hh
  78. 5 2
      thirdparty/harfbuzz/src/hb-cache.hh
  79. 869 0
      thirdparty/harfbuzz/src/hb-cairo-utils.cc
  80. 107 0
      thirdparty/harfbuzz/src/hb-cairo-utils.hh
  81. 1010 0
      thirdparty/harfbuzz/src/hb-cairo.cc
  82. 99 0
      thirdparty/harfbuzz/src/hb-cairo.h
  83. 3 13
      thirdparty/harfbuzz/src/hb-cff-interp-common.hh
  84. 1 2
      thirdparty/harfbuzz/src/hb-cff-interp-cs-common.hh
  85. 1 3
      thirdparty/harfbuzz/src/hb-cff-interp-dict-common.hh
  86. 2 1
      thirdparty/harfbuzz/src/hb-cff1-interp-cs.hh
  87. 9 4
      thirdparty/harfbuzz/src/hb-cff2-interp-cs.hh
  88. 0 26
      thirdparty/harfbuzz/src/hb-common.cc
  89. 26 0
      thirdparty/harfbuzz/src/hb-common.h
  90. 7 1
      thirdparty/harfbuzz/src/hb-config.hh
  91. 0 1
      thirdparty/harfbuzz/src/hb-coretext.cc
  92. 2 0
      thirdparty/harfbuzz/src/hb-cplusplus.hh
  93. 10 6
      thirdparty/harfbuzz/src/hb-debug.hh
  94. 2 1
      thirdparty/harfbuzz/src/hb-deprecated.h
  95. 4 8
      thirdparty/harfbuzz/src/hb-directwrite.cc
  96. 63 2
      thirdparty/harfbuzz/src/hb-draw.cc
  97. 25 10
      thirdparty/harfbuzz/src/hb-draw.h
  98. 246 0
      thirdparty/harfbuzz/src/hb-face-builder.cc
  99. 32 216
      thirdparty/harfbuzz/src/hb-face.cc
  100. 6 0
      thirdparty/harfbuzz/src/hb-face.h

+ 13 - 9
COPYRIGHT.txt

@@ -227,19 +227,23 @@ License: Expat
 
 Files: ./thirdparty/harfbuzz/
 Comment: HarfBuzz text shaping library
-Copyright: 2010-2020, Google, Inc.
- 2018-2020, Ebrahim Byagowi
- 2019-2020, Facebook, Inc.
- 2012, Mozilla Foundation
+Copyright: 2010-2022, Google, Inc.
+ 2015-2020, Ebrahim Byagowi
+ 2019,2020, Facebook, Inc.
+ 2012, 2015, Mozilla Foundation
  2011, Codethink Limited
  2008, 2010, Nokia Corporation and/or its subsidiary(-ies)
  2009, Keith Stribley
- 2009, Martin Hosken and SIL International
+ 2011, Martin Hosken and SIL International
  2007, Chris Wilson
- 2005-2006, 2020-2021, Behdad Esfahbod
- 2005, David Turner
- 2004, 2007-2010, Red Hat, Inc.
- 1998-2004, David Turner and Werner Lemberg
+ 2005-2006, 2020-2023, Behdad Esfahbod
+ 2004, 2007-2010, 2013, 2021-2023, Red Hat, Inc.
+ 1998-2005, David Turner and Werner Lemberg
+ 2016, Igalia, S.L.
+ 2022, Matthias Clasen
+ 2018, 2021, Khaled Hosny
+ 2018-2020, Adobe, Inc.
+ 2013-2015, Alexei Podtelezhnikov
 License: HarfBuzz
 
 Files: ./thirdparty/icu4c/

+ 13 - 6
modules/text_server_adv/SCsub

@@ -53,16 +53,19 @@ if env["builtin_harfbuzz"]:
         "src/hb-buffer-serialize.cc",
         "src/hb-buffer-verify.cc",
         "src/hb-buffer.cc",
+        # "src/hb-cairo-utils.cc",
+        # "src/hb-cairo.cc",
         "src/hb-common.cc",
-        #'src/hb-coretext.cc',
-        #'src/hb-directwrite.cc',
+        # "src/hb-coretext.cc",
+        # "src/hb-directwrite.cc",
         "src/hb-draw.cc",
+        "src/hb-face-builder.cc",
         "src/hb-face.cc",
         "src/hb-fallback-shape.cc",
         "src/hb-font.cc",
-        #'src/hb-gdi.cc',
-        #'src/hb-glib.cc',
-        #'src/hb-gobject-structs.cc',
+        # "src/hb-gdi.cc",
+        # "src/hb-glib.cc",
+        # "src/hb-gobject-structs.cc",
         "src/hb-icu.cc",
         "src/hb-map.cc",
         "src/hb-number.cc",
@@ -94,6 +97,9 @@ if env["builtin_harfbuzz"]:
         "src/hb-ot-shape.cc",
         "src/hb-ot-tag.cc",
         "src/hb-ot-var.cc",
+        "src/hb-outline.cc",
+        "src/hb-paint-extents.cc",
+        "src/hb-paint.cc",
         "src/hb-set.cc",
         "src/hb-shape-plan.cc",
         "src/hb-shape.cc",
@@ -104,12 +110,13 @@ if env["builtin_harfbuzz"]:
         "src/hb-subset-cff1.cc",
         "src/hb-subset-cff2.cc",
         "src/hb-subset-input.cc",
+        "src/hb-subset-instancer-solver.cc",
         "src/hb-subset-plan.cc",
         "src/hb-subset-repacker.cc",
         "src/hb-subset.cc",
         "src/hb-ucd.cc",
         "src/hb-unicode.cc",
-        #'src/hb-uniscribe.cc'
+        # "src/hb-uniscribe.cc",
     ]
 
     if freetype_enabled:

+ 14 - 6
modules/text_server_adv/gdextension_build/SConstruct

@@ -290,16 +290,19 @@ thirdparty_harfbuzz_sources = [
     "src/hb-buffer-serialize.cc",
     "src/hb-buffer-verify.cc",
     "src/hb-buffer.cc",
+    # "src/hb-cairo-utils.cc",
+    # "src/hb-cairo.cc",
     "src/hb-common.cc",
-    #'src/hb-coretext.cc',
-    #'src/hb-directwrite.cc',
+    # "src/hb-coretext.cc",
+    # "src/hb-directwrite.cc",
     "src/hb-draw.cc",
+    "src/hb-face-builder.cc",
     "src/hb-face.cc",
     "src/hb-fallback-shape.cc",
     "src/hb-font.cc",
-    #'src/hb-gdi.cc',
-    #'src/hb-glib.cc',
-    #'src/hb-gobject-structs.cc',
+    # "src/hb-gdi.cc",
+    # "src/hb-glib.cc",
+    # "src/hb-gobject-structs.cc",
     "src/hb-icu.cc",
     "src/hb-map.cc",
     "src/hb-number.cc",
@@ -331,6 +334,9 @@ thirdparty_harfbuzz_sources = [
     "src/hb-ot-shape.cc",
     "src/hb-ot-tag.cc",
     "src/hb-ot-var.cc",
+    "src/hb-outline.cc",
+    "src/hb-paint-extents.cc",
+    "src/hb-paint.cc",
     "src/hb-set.cc",
     "src/hb-shape-plan.cc",
     "src/hb-shape.cc",
@@ -341,11 +347,13 @@ thirdparty_harfbuzz_sources = [
     "src/hb-subset-cff1.cc",
     "src/hb-subset-cff2.cc",
     "src/hb-subset-input.cc",
+    "src/hb-subset-instancer-solver.cc",
     "src/hb-subset-plan.cc",
+    "src/hb-subset-repacker.cc",
     "src/hb-subset.cc",
     "src/hb-ucd.cc",
     "src/hb-unicode.cc",
-    #'src/hb-uniscribe.cc'
+    # "src/hb-uniscribe.cc",
 ]
 
 if env["freetype_enabled"]:

+ 4 - 2
thirdparty/README.md

@@ -248,13 +248,15 @@ Files extracted from upstream source:
 ## harfbuzz
 
 - Upstream: https://github.com/harfbuzz/harfbuzz
-- Version: 6.0.0 (afcae83a064843d71d47624bc162e121cc56c08b, 2022)
+- Version: 7.1.0 (60841e26187576bff477c1a09ee2ffe544844abc, 2023)
 - License: MIT
 
 Files extracted from upstream source:
 
-- the `src` folder
 - `AUTHORS`, `COPYING`, `THANKS`
+- from the `src` folder, recursively
+  - all the `*.c`, `*.cc`, `*.h`, `*.hh` files
+  - _except_ `main.cc`, `harfbuzz*.cc`, `failing-alloc.c`, `test*.cc`
 
 
 ## icu4c

+ 12 - 8
thirdparty/harfbuzz/COPYING

@@ -2,19 +2,23 @@ HarfBuzz is licensed under the so-called "Old MIT" license.  Details follow.
 For parts of HarfBuzz that are licensed under different licenses see individual
 files names COPYING in subdirectories where applicable.
 
-Copyright © 2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020  Google, Inc.
-Copyright © 2018,2019,2020  Ebrahim Byagowi
+Copyright © 2010-2022  Google, Inc.
+Copyright © 2015-2020  Ebrahim Byagowi
 Copyright © 2019,2020  Facebook, Inc.
-Copyright © 2012  Mozilla Foundation
+Copyright © 2012,2015  Mozilla Foundation
 Copyright © 2011  Codethink Limited
 Copyright © 2008,2010  Nokia Corporation and/or its subsidiary(-ies)
 Copyright © 2009  Keith Stribley
-Copyright © 2009  Martin Hosken and SIL International
+Copyright © 2011  Martin Hosken and SIL International
 Copyright © 2007  Chris Wilson
-Copyright © 2005,2006,2020,2021  Behdad Esfahbod
-Copyright © 2005  David Turner
-Copyright © 2004,2007,2008,2009,2010  Red Hat, Inc.
-Copyright © 1998-2004  David Turner and Werner Lemberg
+Copyright © 2005,2006,2020,2021,2022,2023  Behdad Esfahbod
+Copyright © 2004,2007,2008,2009,2010,2013,2021,2022,2023  Red Hat, Inc.
+Copyright © 1998-2005  David Turner and Werner Lemberg
+Copyright © 2016  Igalia S.L.
+Copyright © 2022  Matthias Clasen
+Copyright © 2018,2021  Khaled Hosny
+Copyright © 2018,2019,2020  Adobe, Inc
+Copyright © 2013-2015  Alexei Podtelezhnikov
 
 For full copyright notices consult the individual files in the package.
 

+ 55 - 22
thirdparty/harfbuzz/src/hb-ot-color-cbdt-table.hh → thirdparty/harfbuzz/src/OT/Color/CBDT/CBDT.hh

@@ -24,10 +24,11 @@
  * Google Author(s): Seigo Nonaka, Calder Kitagawa
  */
 
-#ifndef HB_OT_COLOR_CBDT_TABLE_HH
-#define HB_OT_COLOR_CBDT_TABLE_HH
+#ifndef OT_COLOR_CBDT_CBDT_HH
+#define OT_COLOR_CBDT_CBDT_HH
 
-#include "hb-open-type.hh"
+#include "../../../hb-open-type.hh"
+#include "../../../hb-paint.hh"
 
 /*
  * CBLC -- Color Bitmap Location
@@ -80,12 +81,15 @@ struct SmallGlyphMetrics
     return_trace (c->check_struct (this));
   }
 
-  void get_extents (hb_font_t *font, hb_glyph_extents_t *extents) const
+  void get_extents (hb_font_t *font, hb_glyph_extents_t *extents, bool scale) const
   {
-    extents->x_bearing = font->em_scale_x (bearingX);
-    extents->y_bearing = font->em_scale_y (bearingY);
-    extents->width = font->em_scale_x (width);
-    extents->height = font->em_scale_y (-static_cast<int>(height));
+    extents->x_bearing = bearingX;
+    extents->y_bearing = bearingY;
+    extents->width = width;
+    extents->height = -static_cast<int> (height);
+
+    if (scale)
+      font->scale_glyph_extents (extents);
   }
 
   HBUINT8	height;
@@ -307,7 +311,7 @@ struct IndexSubtable
     }
   }
 
-  bool get_extents (hb_glyph_extents_t *extents HB_UNUSED) const
+  bool get_extents (hb_glyph_extents_t *extents HB_UNUSED, bool scale HB_UNUSED) const
   {
     switch (u.header.indexFormat)
     {
@@ -504,8 +508,8 @@ struct IndexSubtableRecord
     return num_missing;
   }
 
-  bool get_extents (hb_glyph_extents_t *extents, const void *base) const
-  { return (base+offsetToSubtable).get_extents (extents); }
+  bool get_extents (hb_glyph_extents_t *extents, const void *base, bool scale) const
+  { return (base+offsetToSubtable).get_extents (extents, scale); }
 
   bool get_image_data (unsigned int  gid,
 		       const void   *base,
@@ -833,7 +837,7 @@ struct CBDT
     }
 
     bool
-    get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const
+    get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents, bool scale = true) const
     {
       const void *base;
       const BitmapSizeTable &strike = this->cblc->choose_strike (font);
@@ -841,7 +845,7 @@ struct CBDT
       if (!subtable_record || !strike.ppemX || !strike.ppemY)
 	return false;
 
-      if (subtable_record->get_extents (extents, base))
+      if (subtable_record->get_extents (extents, base, scale))
 	return true;
 
       unsigned int image_offset = 0, image_length = 0, image_format = 0;
@@ -858,26 +862,29 @@ struct CBDT
 	if (unlikely (image_length < GlyphBitmapDataFormat17::min_size))
 	  return false;
 	auto &glyphFormat17 = StructAtOffset<GlyphBitmapDataFormat17> (this->cbdt, image_offset);
-	glyphFormat17.glyphMetrics.get_extents (font, extents);
+	glyphFormat17.glyphMetrics.get_extents (font, extents, scale);
 	break;
       }
       case 18: {
 	if (unlikely (image_length < GlyphBitmapDataFormat18::min_size))
 	  return false;
 	auto &glyphFormat18 = StructAtOffset<GlyphBitmapDataFormat18> (this->cbdt, image_offset);
-	glyphFormat18.glyphMetrics.get_extents (font, extents);
+	glyphFormat18.glyphMetrics.get_extents (font, extents, scale);
 	break;
       }
       default: return false; /* TODO: Support other image formats. */
       }
 
       /* Convert to font units. */
-      float x_scale = upem / (float) strike.ppemX;
-      float y_scale = upem / (float) strike.ppemY;
-      extents->x_bearing = roundf (extents->x_bearing * x_scale);
-      extents->y_bearing = roundf (extents->y_bearing * y_scale);
-      extents->width = roundf (extents->width * x_scale);
-      extents->height = roundf (extents->height * y_scale);
+      if (scale)
+      {
+	float x_scale = upem / (float) strike.ppemX;
+	float y_scale = upem / (float) strike.ppemY;
+	extents->x_bearing = roundf (extents->x_bearing * x_scale);
+	extents->y_bearing = roundf (extents->y_bearing * y_scale);
+	extents->width = roundf (extents->width * x_scale);
+	extents->height = roundf (extents->height * y_scale);
+      }
 
       return true;
     }
@@ -934,6 +941,32 @@ struct CBDT
 
     bool has_data () const { return cbdt.get_length (); }
 
+    bool paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data) const
+    {
+      hb_glyph_extents_t extents;
+      hb_glyph_extents_t pixel_extents;
+      hb_blob_t *blob = reference_png (font, glyph);
+
+      if (unlikely (blob == hb_blob_get_empty ()))
+        return false;
+
+      if (unlikely (!hb_font_get_glyph_extents (font, glyph, &extents)))
+        return false;
+
+      if (unlikely (!get_extents (font, glyph, &pixel_extents, false)))
+        return false;
+
+      bool ret = funcs->image (data,
+			       blob,
+			       pixel_extents.width, -pixel_extents.height,
+			       HB_PAINT_IMAGE_FORMAT_PNG,
+			       font->slant_xy,
+			       &extents);
+
+      hb_blob_destroy (blob);
+      return ret;
+    }
+
     private:
     hb_blob_ptr_t<CBLC> cblc;
     hb_blob_ptr_t<CBDT> cbdt;
@@ -994,4 +1027,4 @@ struct CBDT_accelerator_t : CBDT::accelerator_t {
 
 } /* namespace OT */
 
-#endif /* HB_OT_COLOR_CBDT_TABLE_HH */
+#endif /* OT_COLOR_CBDT_CBDT_HH */

+ 607 - 45
thirdparty/harfbuzz/src/hb-ot-color-colr-table.hh → thirdparty/harfbuzz/src/OT/Color/COLR/COLR.hh

@@ -25,12 +25,14 @@
  * Google Author(s): Calder Kitagawa
  */
 
-#ifndef HB_OT_COLOR_COLR_TABLE_HH
-#define HB_OT_COLOR_COLR_TABLE_HH
+#ifndef OT_COLOR_COLR_COLR_HH
+#define OT_COLOR_COLR_COLR_HH
 
-#include "hb-open-type.hh"
-#include "hb-ot-layout-common.hh"
-#include "hb-ot-var-common.hh"
+#include "../../../hb.hh"
+#include "../../../hb-open-type.hh"
+#include "../../../hb-ot-var-common.hh"
+#include "../../../hb-paint.hh"
+#include "../../../hb-paint-extents.hh"
 
 /*
  * COLR -- Color
@@ -38,13 +40,82 @@
  */
 #define HB_OT_TAG_COLR HB_TAG('C','O','L','R')
 
-#ifndef HB_COLRV1_MAX_NESTING_LEVEL
-#define HB_COLRV1_MAX_NESTING_LEVEL	16
-#endif
+
+namespace OT {
+struct hb_paint_context_t;
+}
 
 namespace OT {
 
 struct COLR;
+
+struct Paint;
+
+struct hb_paint_context_t :
+       hb_dispatch_context_t<hb_paint_context_t>
+{
+  template <typename T>
+  return_t dispatch (const T &obj) { obj.paint_glyph (this); return hb_empty_t (); }
+  static return_t default_return_value () { return hb_empty_t (); }
+
+  const COLR* get_colr_table () const
+  { return reinterpret_cast<const COLR *> (base); }
+
+public:
+  const void *base;
+  hb_paint_funcs_t *funcs;
+  void *data;
+  hb_font_t *font;
+  unsigned int palette_index;
+  hb_color_t foreground;
+  VarStoreInstancer &instancer;
+  int depth_left = HB_MAX_NESTING_LEVEL;
+  int edge_count = HB_COLRV1_MAX_EDGE_COUNT;
+
+  hb_paint_context_t (const void *base_,
+		      hb_paint_funcs_t *funcs_,
+		      void *data_,
+                      hb_font_t *font_,
+                      unsigned int palette_,
+                      hb_color_t foreground_,
+		      VarStoreInstancer &instancer_) :
+    base (base_),
+    funcs (funcs_),
+    data (data_),
+    font (font_),
+    palette_index (palette_),
+    foreground (foreground_),
+    instancer (instancer_)
+  { }
+
+  hb_color_t get_color (unsigned int color_index, float alpha, hb_bool_t *is_foreground)
+  {
+    hb_color_t color = foreground;
+
+    *is_foreground = true;
+
+    if (color_index != 0xffff)
+    {
+      if (!funcs->custom_palette_color (data, color_index, &color))
+      {
+	unsigned int clen = 1;
+	hb_face_t *face = hb_font_get_face (font);
+
+	hb_ot_color_palette_get_colors (face, palette_index, color_index, &clen, &color);
+      }
+
+      *is_foreground = false;
+    }
+
+    return HB_COLOR (hb_color_get_blue (color),
+                     hb_color_get_green (color),
+                     hb_color_get_red (color),
+                     hb_color_get_alpha (color) * alpha);
+  }
+
+  inline void recurse (const Paint &paint);
+};
+
 struct hb_colrv1_closure_context_t :
        hb_dispatch_context_t<hb_colrv1_closure_context_t>
 {
@@ -98,7 +169,7 @@ struct hb_colrv1_closure_context_t :
                                hb_set_t *glyphs_,
                                hb_set_t *layer_indices_,
                                hb_set_t *palette_indices_,
-                               unsigned nesting_level_left_ = HB_COLRV1_MAX_NESTING_LEVEL) :
+                               unsigned nesting_level_left_ = HB_MAX_NESTING_LEVEL) :
                           base (base_),
                           glyphs (glyphs_),
                           layer_indices (layer_indices_),
@@ -160,6 +231,8 @@ struct BaseGlyphRecord
 template <typename T>
 struct Variable
 {
+  static constexpr bool is_variable = true;
+
   Variable<T>* copy (hb_serialize_context_t *c) const
   {
     TRACE_SERIALIZE (this);
@@ -182,6 +255,23 @@ struct Variable
     return_trace (c->check_struct (this) && value.sanitize (c));
   }
 
+  void paint_glyph (hb_paint_context_t *c) const
+  {
+    value.paint_glyph (c, varIdxBase);
+  }
+
+  void get_color_stop (hb_paint_context_t *c,
+                       hb_color_stop_t *stop,
+		       const VarStoreInstancer &instancer) const
+  {
+    value.get_color_stop (c, stop, varIdxBase, instancer);
+  }
+
+  hb_paint_extend_t get_extend () const
+  {
+    return value.get_extend ();
+  }
+
   protected:
   T      value;
   public:
@@ -193,6 +283,8 @@ struct Variable
 template <typename T>
 struct NoVariable
 {
+  static constexpr bool is_variable = false;
+
   static constexpr uint32_t varIdxBase = VarIdx::NO_VARIATION;
 
   NoVariable<T>* copy (hb_serialize_context_t *c) const
@@ -216,6 +308,23 @@ struct NoVariable
     return_trace (c->check_struct (this) && value.sanitize (c));
   }
 
+  void paint_glyph (hb_paint_context_t *c) const
+  {
+    value.paint_glyph (c, varIdxBase);
+  }
+
+  void get_color_stop (hb_paint_context_t *c,
+                       hb_color_stop_t *stop,
+		       const VarStoreInstancer &instancer) const
+  {
+    value.get_color_stop (c, stop, VarIdx::NO_VARIATION, instancer);
+  }
+
+  hb_paint_extend_t get_extend () const
+  {
+    return value.get_extend ();
+  }
+
   T      value;
   public:
   DEFINE_SIZE_STATIC (T::static_size);
@@ -233,7 +342,7 @@ struct ColorStop
     TRACE_SUBSET (this);
     auto *out = c->serializer->embed (*this);
     if (unlikely (!out)) return_trace (false);
-    return_trace (c->serializer->check_assign (out->paletteIndex, c->plan->colr_palettes->get (paletteIndex),
+    return_trace (c->serializer->check_assign (out->paletteIndex, c->plan->colr_palettes.get (paletteIndex),
                                                HB_SERIALIZE_ERROR_INT_OVERFLOW));
   }
 
@@ -243,6 +352,17 @@ struct ColorStop
     return_trace (c->check_struct (this));
   }
 
+  void get_color_stop (hb_paint_context_t *c,
+                       hb_color_stop_t *out,
+		       uint32_t varIdx,
+		       const VarStoreInstancer &instancer) const
+  {
+    out->offset = stopOffset.to_float(instancer (varIdx, 0));
+    out->color = c->get_color (paletteIndex,
+                               alpha.to_float (instancer (varIdx, 1)),
+                               &out->is_foreground);
+  }
+
   F2DOT14	stopOffset;
   HBUINT16	paletteIndex;
   F2DOT14	alpha;
@@ -294,6 +414,52 @@ struct ColorLine
                   stops.sanitize (c));
   }
 
+  /* get up to count stops from start */
+  unsigned int
+  get_color_stops (hb_paint_context_t *c,
+                   unsigned int start,
+		   unsigned int *count,
+		   hb_color_stop_t *color_stops,
+		   const VarStoreInstancer &instancer) const
+  {
+    unsigned int len = stops.len;
+
+    if (count && color_stops)
+    {
+      unsigned int i;
+      for (i = 0; i < *count && start + i < len; i++)
+        stops[start + i].get_color_stop (c, &color_stops[i], instancer);
+      *count = i;
+    }
+
+    return len;
+  }
+
+  HB_INTERNAL static unsigned int static_get_color_stops (hb_color_line_t *color_line,
+							  void *color_line_data,
+							  unsigned int start,
+							  unsigned int *count,
+							  hb_color_stop_t *color_stops,
+							  void *user_data)
+  {
+    const ColorLine *thiz = (const ColorLine *) color_line_data;
+    hb_paint_context_t *c = (hb_paint_context_t *) user_data;
+    return thiz->get_color_stops (c, start, count, color_stops, c->instancer);
+  }
+
+  hb_paint_extend_t get_extend () const
+  {
+    return (hb_paint_extend_t) (unsigned int) extend;
+  }
+
+  HB_INTERNAL static hb_paint_extend_t static_get_extend (hb_color_line_t *color_line,
+							  void *color_line_data,
+							  void *user_data)
+  {
+    const ColorLine *thiz = (const ColorLine *) color_line_data;
+    return thiz->get_extend ();
+  }
+
   Extend	extend;
   Array16Of<Var<ColorStop>>	stops;
   public:
@@ -357,6 +523,17 @@ struct Affine2x3
     return_trace (c->check_struct (this));
   }
 
+  void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
+  {
+    c->funcs->push_transform (c->data,
+			      xx.to_float (c->instancer (varIdxBase, 0)),
+			      yx.to_float (c->instancer (varIdxBase, 1)),
+                              xy.to_float (c->instancer (varIdxBase, 2)),
+			      yy.to_float (c->instancer (varIdxBase, 3)),
+                              dx.to_float (c->instancer (varIdxBase, 4)),
+			      dy.to_float (c->instancer (varIdxBase, 5)));
+  }
+
   F16DOT16 xx;
   F16DOT16 yx;
   F16DOT16 xy;
@@ -376,7 +553,7 @@ struct PaintColrLayers
     TRACE_SUBSET (this);
     auto *out = c->serializer->embed (this);
     if (unlikely (!out)) return_trace (false);
-    return_trace (c->serializer->check_assign (out->firstLayerIndex, c->plan->colrv1_layers->get (firstLayerIndex),
+    return_trace (c->serializer->check_assign (out->firstLayerIndex, c->plan->colrv1_layers.get (firstLayerIndex),
                                                HB_SERIALIZE_ERROR_INT_OVERFLOW));
 
     return_trace (true);
@@ -388,6 +565,8 @@ struct PaintColrLayers
     return_trace (c->check_struct (this));
   }
 
+  inline void paint_glyph (hb_paint_context_t *c) const;
+
   HBUINT8	format; /* format = 1 */
   HBUINT8	numLayers;
   HBUINT32	firstLayerIndex;  /* index into COLRv1::layerList */
@@ -405,7 +584,7 @@ struct PaintSolid
     TRACE_SUBSET (this);
     auto *out = c->serializer->embed (*this);
     if (unlikely (!out)) return_trace (false);
-    return_trace (c->serializer->check_assign (out->paletteIndex, c->plan->colr_palettes->get (paletteIndex),
+    return_trace (c->serializer->check_assign (out->paletteIndex, c->plan->colr_palettes.get (paletteIndex),
                                                HB_SERIALIZE_ERROR_INT_OVERFLOW));
   }
 
@@ -415,6 +594,17 @@ struct PaintSolid
     return_trace (c->check_struct (this));
   }
 
+  void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
+  {
+    hb_bool_t is_foreground;
+    hb_color_t color;
+
+    color = c->get_color (paletteIndex,
+                          alpha.to_float (c->instancer (varIdxBase, 0)),
+                          &is_foreground);
+    c->funcs->color (c->data, is_foreground, color);
+  }
+
   HBUINT8	format; /* format = 2(noVar) or 3(Var)*/
   HBUINT16	paletteIndex;
   F2DOT14	alpha;
@@ -443,6 +633,23 @@ struct PaintLinearGradient
     return_trace (c->check_struct (this) && colorLine.sanitize (c, this));
   }
 
+  void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
+  {
+    hb_color_line_t cl = {
+      (void *) &(this+colorLine),
+      (this+colorLine).static_get_color_stops, c,
+      (this+colorLine).static_get_extend, nullptr
+    };
+
+    c->funcs->linear_gradient (c->data, &cl,
+			       x0 + c->instancer (varIdxBase, 0),
+			       y0 + c->instancer (varIdxBase, 1),
+			       x1 + c->instancer (varIdxBase, 2),
+			       y1 + c->instancer (varIdxBase, 3),
+			       x2 + c->instancer (varIdxBase, 4),
+			       y2 + c->instancer (varIdxBase, 5));
+  }
+
   HBUINT8			format; /* format = 4(noVar) or 5 (Var) */
   Offset24To<ColorLine<Var>>	colorLine; /* Offset (from beginning of PaintLinearGradient
                                             * table) to ColorLine subtable. */
@@ -477,6 +684,23 @@ struct PaintRadialGradient
     return_trace (c->check_struct (this) && colorLine.sanitize (c, this));
   }
 
+  void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
+  {
+    hb_color_line_t cl = {
+      (void *) &(this+colorLine),
+      (this+colorLine).static_get_color_stops, c,
+      (this+colorLine).static_get_extend, nullptr
+    };
+
+    c->funcs->radial_gradient (c->data, &cl,
+			       x0 + c->instancer (varIdxBase, 0),
+			       y0 + c->instancer (varIdxBase, 1),
+			       radius0 + c->instancer (varIdxBase, 2),
+			       x1 + c->instancer (varIdxBase, 3),
+			       y1 + c->instancer (varIdxBase, 4),
+			       radius1 + c->instancer (varIdxBase, 5));
+  }
+
   HBUINT8			format; /* format = 6(noVar) or 7 (Var) */
   Offset24To<ColorLine<Var>>	colorLine; /* Offset (from beginning of PaintRadialGradient
                                             * table) to ColorLine subtable. */
@@ -511,6 +735,21 @@ struct PaintSweepGradient
     return_trace (c->check_struct (this) && colorLine.sanitize (c, this));
   }
 
+  void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
+  {
+    hb_color_line_t cl = {
+      (void *) &(this+colorLine),
+      (this+colorLine).static_get_color_stops, c,
+      (this+colorLine).static_get_extend, nullptr
+    };
+
+    c->funcs->sweep_gradient (c->data, &cl,
+			      centerX + c->instancer (varIdxBase, 0),
+			      centerY + c->instancer (varIdxBase, 1),
+                              (startAngle.to_float (c->instancer (varIdxBase, 2)) + 1) * (float) M_PI,
+                              (endAngle.to_float   (c->instancer (varIdxBase, 3)) + 1) * (float) M_PI);
+  }
+
   HBUINT8			format; /* format = 8(noVar) or 9 (Var) */
   Offset24To<ColorLine<Var>>	colorLine; /* Offset (from beginning of PaintSweepGradient
                                             * table) to ColorLine subtable. */
@@ -522,8 +761,6 @@ struct PaintSweepGradient
   DEFINE_SIZE_STATIC (4 + 2 * FWORD::static_size + 2 * F2DOT14::static_size);
 };
 
-struct Paint;
-
 // Paint a non-COLR glyph, filled as indicated by paint.
 struct PaintGlyph
 {
@@ -548,6 +785,17 @@ struct PaintGlyph
     return_trace (c->check_struct (this) && paint.sanitize (c, this));
   }
 
+  void paint_glyph (hb_paint_context_t *c) const
+  {
+    c->funcs->push_inverse_root_transform (c->data, c->font);
+    c->funcs->push_clip_glyph (c->data, gid, c->font);
+    c->funcs->push_root_transform (c->data, c->font);
+    c->recurse (this+paint);
+    c->funcs->pop_transform (c->data);
+    c->funcs->pop_clip (c->data);
+    c->funcs->pop_transform (c->data);
+  }
+
   HBUINT8		format; /* format = 10 */
   Offset24To<Paint>	paint;  /* Offset (from beginning of PaintGlyph table) to Paint subtable. */
   HBUINT16		gid;
@@ -575,6 +823,8 @@ struct PaintColrGlyph
     return_trace (c->check_struct (this));
   }
 
+  inline void paint_glyph (hb_paint_context_t *c) const;
+
   HBUINT8	format; /* format = 11 */
   HBUINT16	gid;
   public:
@@ -603,6 +853,13 @@ struct PaintTransform
                   transform.sanitize (c, this));
   }
 
+  void paint_glyph (hb_paint_context_t *c) const
+  {
+    (this+transform).paint_glyph (c);
+    c->recurse (this+src);
+    c->funcs->pop_transform (c->data);
+  }
+
   HBUINT8			format; /* format = 12(noVar) or 13 (Var) */
   Offset24To<Paint>		src; /* Offset (from beginning of PaintTransform table) to Paint subtable. */
   Offset24To<Var<Affine2x3>>	transform;
@@ -629,6 +886,16 @@ struct PaintTranslate
     return_trace (c->check_struct (this) && src.sanitize (c, this));
   }
 
+  void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
+  {
+    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->recurse (this+src);
+    if (p1) c->funcs->pop_transform (c->data);
+  }
+
   HBUINT8		format; /* format = 14(noVar) or 15 (Var) */
   Offset24To<Paint>	src; /* Offset (from beginning of PaintTranslate table) to Paint subtable. */
   FWORD		dx;
@@ -656,6 +923,16 @@ struct PaintScale
     return_trace (c->check_struct (this) && src.sanitize (c, this));
   }
 
+  void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
+  {
+    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->recurse (this+src);
+    if (p1) c->funcs->pop_transform (c->data);
+  }
+
   HBUINT8		format; /* format = 16 (noVar) or 17(Var) */
   Offset24To<Paint>	src; /* Offset (from beginning of PaintScale table) to Paint subtable. */
   F2DOT14		scaleX;
@@ -683,6 +960,22 @@ struct PaintScaleAroundCenter
     return_trace (c->check_struct (this) && src.sanitize (c, this));
   }
 
+  void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
+  {
+    float sx = scaleX.to_float (c->instancer (varIdxBase, 0));
+    float sy = scaleY.to_float (c->instancer (varIdxBase, 1));
+    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->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);
+  }
+
   HBUINT8		format; /* format = 18 (noVar) or 19(Var) */
   Offset24To<Paint>	src; /* Offset (from beginning of PaintScaleAroundCenter table) to Paint subtable. */
   F2DOT14	scaleX;
@@ -712,6 +1005,15 @@ struct PaintScaleUniform
     return_trace (c->check_struct (this) && src.sanitize (c, this));
   }
 
+  void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
+  {
+    float s = scale.to_float (c->instancer (varIdxBase, 0));
+
+    bool p1 = c->funcs->push_scale (c->data, s, s);
+    c->recurse (this+src);
+    if (p1) c->funcs->pop_transform (c->data);
+  }
+
   HBUINT8		format; /* format = 20 (noVar) or 21(Var) */
   Offset24To<Paint>	src; /* Offset (from beginning of PaintScaleUniform table) to Paint subtable. */
   F2DOT14		scale;
@@ -738,6 +1040,21 @@ struct PaintScaleUniformAroundCenter
     return_trace (c->check_struct (this) && src.sanitize (c, this));
   }
 
+  void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
+  {
+    float s = scale.to_float (c->instancer (varIdxBase, 0));
+    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->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);
+  }
+
   HBUINT8		format; /* format = 22 (noVar) or 23(Var) */
   Offset24To<Paint>	src; /* Offset (from beginning of PaintScaleUniformAroundCenter table) to Paint subtable. */
   F2DOT14	scale;
@@ -766,6 +1083,15 @@ struct PaintRotate
     return_trace (c->check_struct (this) && src.sanitize (c, this));
   }
 
+  void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
+  {
+    float a = angle.to_float (c->instancer (varIdxBase, 0));
+
+    bool p1 = c->funcs->push_rotate (c->data, a);
+    c->recurse (this+src);
+    if (p1) c->funcs->pop_transform (c->data);
+  }
+
   HBUINT8		format; /* format = 24 (noVar) or 25(Var) */
   Offset24To<Paint>	src; /* Offset (from beginning of PaintRotate table) to Paint subtable. */
   F2DOT14		angle;
@@ -792,6 +1118,21 @@ struct PaintRotateAroundCenter
     return_trace (c->check_struct (this) && src.sanitize (c, this));
   }
 
+  void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
+  {
+    float a = angle.to_float (c->instancer (varIdxBase, 0));
+    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->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);
+  }
+
   HBUINT8		format; /* format = 26 (noVar) or 27(Var) */
   Offset24To<Paint>	src; /* Offset (from beginning of PaintRotateAroundCenter table) to Paint subtable. */
   F2DOT14	angle;
@@ -820,6 +1161,16 @@ struct PaintSkew
     return_trace (c->check_struct (this) && src.sanitize (c, this));
   }
 
+  void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
+  {
+    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->recurse (this+src);
+    if (p1) c->funcs->pop_transform (c->data);
+  }
+
   HBUINT8		format; /* format = 28(noVar) or 29 (Var) */
   Offset24To<Paint>	src; /* Offset (from beginning of PaintSkew table) to Paint subtable. */
   F2DOT14		xSkewAngle;
@@ -847,6 +1198,22 @@ struct PaintSkewAroundCenter
     return_trace (c->check_struct (this) && src.sanitize (c, this));
   }
 
+  void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const
+  {
+    float sx = xSkewAngle.to_float(c->instancer (varIdxBase, 0));
+    float sy = ySkewAngle.to_float(c->instancer (varIdxBase, 1));
+    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->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);
+  }
+
   HBUINT8		format; /* format = 30(noVar) or 31 (Var) */
   Offset24To<Paint>	src; /* Offset (from beginning of PaintSkewAroundCenter table) to Paint subtable. */
   F2DOT14	xSkewAngle;
@@ -879,6 +1246,14 @@ struct PaintComposite
                   backdrop.sanitize (c, this));
   }
 
+  void paint_glyph (hb_paint_context_t *c) const
+  {
+    c->recurse (this+backdrop);
+    c->funcs->push_group (c->data);
+    c->recurse (this+src);
+    c->funcs->pop_group (c->data, (hb_paint_composite_mode_t) (int) mode);
+  }
+
   HBUINT8		format; /* format = 32 */
   Offset24To<Paint>	src; /* Offset (from beginning of PaintComposite table) to source Paint subtable. */
   CompositeMode		mode;   /* If mode is unrecognized use COMPOSITE_CLEAR */
@@ -948,8 +1323,8 @@ struct ClipBox
   template <typename context_t, typename ...Ts>
   typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
   {
+    if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value ();
     TRACE_DISPATCH (this, u.format);
-    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
     switch (u.format) {
     case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
     case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...));
@@ -1084,7 +1459,7 @@ struct ClipList
     if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
     if (!c->serializer->check_assign (out->format, format, HB_SERIALIZE_ERROR_INT_OVERFLOW)) return_trace (false);
 
-    const hb_set_t& glyphset = *c->plan->_glyphset_colred;
+    const hb_set_t& glyphset = c->plan->_glyphset_colred;
     const hb_map_t &glyph_map = *c->plan->glyph_map;
 
     hb_map_t new_gid_offset_map;
@@ -1142,7 +1517,7 @@ struct Paint
   {
     TRACE_SANITIZE (this);
 
-    if (unlikely (!c->check_start_recursion (HB_COLRV1_MAX_NESTING_LEVEL)))
+    if (unlikely (!c->check_start_recursion (HB_MAX_NESTING_LEVEL)))
       return_trace (c->no_dispatch_return_value ());
 
     return_trace (c->end_recursion (this->dispatch (c, std::forward<Ts> (ds)...)));
@@ -1151,8 +1526,8 @@ struct Paint
   template <typename context_t, typename ...Ts>
   typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
   {
+    if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value ();
     TRACE_DISPATCH (this, u.format);
-    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
     switch (u.format) {
     case 1: return_trace (c->dispatch (u.paintformat1, std::forward<Ts> (ds)...));
     case 2: return_trace (c->dispatch (u.paintformat2, std::forward<Ts> (ds)...));
@@ -1194,35 +1569,35 @@ struct Paint
   union {
   HBUINT8					format;
   PaintColrLayers				paintformat1;
-  PaintSolid					paintformat2;
+  NoVariable<PaintSolid>			paintformat2;
   Variable<PaintSolid>				paintformat3;
-  PaintLinearGradient<NoVariable>		paintformat4;
+  NoVariable<PaintLinearGradient<NoVariable>>	paintformat4;
   Variable<PaintLinearGradient<Variable>>	paintformat5;
-  PaintRadialGradient<NoVariable>		paintformat6;
+  NoVariable<PaintRadialGradient<NoVariable>>	paintformat6;
   Variable<PaintRadialGradient<Variable>>	paintformat7;
-  PaintSweepGradient<NoVariable>		paintformat8;
+  NoVariable<PaintSweepGradient<NoVariable>>	paintformat8;
   Variable<PaintSweepGradient<Variable>>	paintformat9;
   PaintGlyph					paintformat10;
   PaintColrGlyph				paintformat11;
   PaintTransform<NoVariable>			paintformat12;
   PaintTransform<Variable>			paintformat13;
-  PaintTranslate				paintformat14;
+  NoVariable<PaintTranslate>			paintformat14;
   Variable<PaintTranslate>			paintformat15;
-  PaintScale					paintformat16;
+  NoVariable<PaintScale>			paintformat16;
   Variable<PaintScale>				paintformat17;
-  PaintScaleAroundCenter			paintformat18;
+  NoVariable<PaintScaleAroundCenter>		paintformat18;
   Variable<PaintScaleAroundCenter>		paintformat19;
-  PaintScaleUniform				paintformat20;
+  NoVariable<PaintScaleUniform>			paintformat20;
   Variable<PaintScaleUniform>			paintformat21;
-  PaintScaleUniformAroundCenter			paintformat22;
+  NoVariable<PaintScaleUniformAroundCenter>	paintformat22;
   Variable<PaintScaleUniformAroundCenter>	paintformat23;
-  PaintRotate					paintformat24;
+  NoVariable<PaintRotate>			paintformat24;
   Variable<PaintRotate>				paintformat25;
-  PaintRotateAroundCenter			paintformat26;
+  NoVariable<PaintRotateAroundCenter>		paintformat26;
   Variable<PaintRotateAroundCenter>		paintformat27;
-  PaintSkew					paintformat28;
+  NoVariable<PaintSkew>				paintformat28;
   Variable<PaintSkew>				paintformat29;
-  PaintSkewAroundCenter				paintformat30;
+  NoVariable<PaintSkewAroundCenter>		paintformat30;
   Variable<PaintSkewAroundCenter>		paintformat31;
   PaintComposite				paintformat32;
   } u;
@@ -1269,7 +1644,7 @@ struct BaseGlyphList : SortedArray32Of<BaseGlyphPaintRecord>
     TRACE_SUBSET (this);
     auto *out = c->serializer->start_embed (this);
     if (unlikely (!c->serializer->extend_min (out)))  return_trace (false);
-    const hb_set_t* glyphset = c->plan->_glyphset_colred;
+    const hb_set_t* glyphset = &c->plan->_glyphset_colred;
 
     for (const auto& _ : as_array ())
     {
@@ -1323,7 +1698,14 @@ struct COLR
 {
   static constexpr hb_tag_t tableTag = HB_OT_TAG_COLR;
 
-  bool has_data () const { return numBaseGlyphs; }
+  bool has_v0_data () const { return numBaseGlyphs; }
+  bool has_v1_data () const
+  {
+    if (version == 1)
+      return (this+baseGlyphList).len > 0;
+
+    return false;
+  }
 
   unsigned int get_glyph_layers (hb_codepoint_t       glyph,
 				 unsigned int         start_offset,
@@ -1503,7 +1885,7 @@ struct COLR
     TRACE_SUBSET (this);
 
     const hb_map_t &reverse_glyph_map = *c->plan->reverse_glyph_map;
-    const hb_set_t& glyphset = *c->plan->_glyphset_colred;
+    const hb_set_t& glyphset = c->plan->_glyphset_colred;
 
     auto base_it =
     + hb_range (c->plan->num_output_glyphs ())
@@ -1552,7 +1934,7 @@ struct COLR
 				  if (unlikely (!c->plan->new_gid_for_old_gid (out_layers[i].glyphId, &new_gid)))
 				    return hb_pair_t<bool, hb_vector_t<LayerRecord>> (false, out_layers);
 				  out_layers[i].glyphId = new_gid;
-				  out_layers[i].colorIdx = c->plan->colr_palettes->get (layers[i].colorIdx);
+				  out_layers[i].colorIdx = c->plan->colr_palettes.get (layers[i].colorIdx);
 				}
 
 				return hb_pair_t<bool, hb_vector_t<LayerRecord>> (true, out_layers);
@@ -1585,10 +1967,23 @@ struct COLR
     colr_prime->layerList.serialize_subset (c, layerList, this);
     colr_prime->clipList.serialize_subset (c, clipList, this);
     colr_prime->varIdxMap.serialize_copy (c->serializer, varIdxMap, this);
-    //TODO: subset varStore once it's implemented in fonttools
+    colr_prime->varStore.serialize_copy (c->serializer, varStore, this);
     return_trace (true);
   }
 
+  const Paint *get_base_glyph_paint (hb_codepoint_t glyph) const
+  {
+    const BaseGlyphList &baseglyph_paintrecords = this+baseGlyphList;
+    const BaseGlyphPaintRecord* record = get_base_glyph_paintrecord (glyph);
+    if (record)
+    {
+      const Paint &paint = &baseglyph_paintrecords+record->paint;
+      return &paint;
+    }
+    else
+      return nullptr;
+  }
+
   bool
   get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const
   {
@@ -1599,14 +1994,138 @@ struct COLR
 				 this+varIdxMap,
 				 hb_array (font->coords, font->num_coords));
 
-    if ((this+clipList).get_extents (glyph,
-				     extents,
-				     instancer))
+    if (get_clip (glyph, extents, instancer))
     {
-      extents->x_bearing = font->em_scale_x (extents->x_bearing);
-      extents->y_bearing = font->em_scale_x (extents->y_bearing);
-      extents->width = font->em_scale_x (extents->width);
-      extents->height = font->em_scale_x (extents->height);
+      font->scale_glyph_extents (extents);
+      return true;
+    }
+
+    auto *extents_funcs = hb_paint_extents_get_funcs ();
+    hb_paint_extents_context_t extents_data;
+    bool ret = paint_glyph (font, glyph, extents_funcs, &extents_data, 0, HB_COLOR(0,0,0,0));
+
+    hb_extents_t e = extents_data.get_extents ();
+    if (e.is_void ())
+    {
+      extents->x_bearing = 0;
+      extents->y_bearing = 0;
+      extents->width = 0;
+      extents->height = 0;
+    }
+    else
+    {
+      extents->x_bearing = e.xmin;
+      extents->y_bearing = e.ymax;
+      extents->width = e.xmax - e.xmin;
+      extents->height = e.ymin - e.ymax;
+    }
+
+    return ret;
+  }
+
+  bool
+  has_paint_for_glyph (hb_codepoint_t glyph) const
+  {
+    if (version == 1)
+    {
+      const Paint *paint = get_base_glyph_paint (glyph);
+
+      return paint != nullptr;
+    }
+
+    return false;
+  }
+
+  bool get_clip (hb_codepoint_t glyph,
+		 hb_glyph_extents_t *extents,
+		 const VarStoreInstancer instancer) const
+  {
+    return (this+clipList).get_extents (glyph,
+					extents,
+					instancer);
+  }
+
+  bool
+  paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, unsigned int palette_index, hb_color_t foreground, bool clip = true) const
+  {
+    VarStoreInstancer instancer (this+varStore,
+	                         this+varIdxMap,
+	                         hb_array (font->coords, font->num_coords));
+    hb_paint_context_t c (this, funcs, data, font, palette_index, foreground, instancer);
+
+    if (version == 1)
+    {
+      const Paint *paint = get_base_glyph_paint (glyph);
+      if (paint)
+      {
+        // COLRv1 glyph
+
+	VarStoreInstancer instancer (this+varStore,
+				     this+varIdxMap,
+				     hb_array (font->coords, font->num_coords));
+
+	bool is_bounded = true;
+	if (clip)
+	{
+	  hb_glyph_extents_t extents;
+	  if (get_clip (glyph, &extents, instancer))
+	  {
+	    font->scale_glyph_extents (&extents);
+	    c.funcs->push_clip_rectangle (c.data,
+					  extents.x_bearing,
+					  extents.y_bearing + extents.height,
+					  extents.x_bearing + extents.width,
+					  extents.y_bearing);
+	  }
+	  else
+	  {
+	    auto *extents_funcs = hb_paint_extents_get_funcs ();
+	    hb_paint_extents_context_t extents_data;
+
+	    paint_glyph (font, glyph,
+			 extents_funcs, &extents_data,
+			 palette_index, foreground,
+			 false);
+
+	    hb_extents_t extents = extents_data.get_extents ();
+	    is_bounded = extents_data.is_bounded ();
+
+	    c.funcs->push_clip_rectangle (c.data,
+					  extents.xmin,
+					  extents.ymin,
+					  extents.xmax,
+					  extents.ymax);
+	  }
+	}
+
+	c.funcs->push_root_transform (c.data, font);
+
+	if (is_bounded)
+	  c.recurse (*paint);
+
+	c.funcs->pop_transform (c.data);
+
+	if (clip)
+	  c.funcs->pop_clip (c.data);
+
+        return true;
+      }
+    }
+
+    const BaseGlyphRecord *record = get_base_glyph_record (glyph);
+    if (record && ((hb_codepoint_t) record->glyphId == glyph))
+    {
+      // COLRv0 glyph
+      for (const auto &r : (this+layersZ).as_array (numLayers)
+			   .sub_array (record->firstLayerIdx, record->numLayers))
+      {
+        hb_bool_t is_foreground;
+        hb_color_t color = c.get_color (r.colorIdx, 1., &is_foreground);
+        c.funcs->push_clip_glyph (c.data, r.glyphId, c.font);
+        c.funcs->color (c.data, is_foreground, color);
+        c.funcs->pop_clip (c.data);
+      }
+
       return true;
     }
 
@@ -1635,7 +2154,50 @@ struct COLR_accelerator_t : COLR::accelerator_t {
   COLR_accelerator_t (hb_face_t *face) : COLR::accelerator_t (face) {}
 };
 
-} /* namespace OT */
+void
+hb_paint_context_t::recurse (const Paint &paint)
+{
+  if (unlikely (depth_left <= 0 || edge_count <= 0)) return;
+  depth_left--;
+  edge_count--;
+  paint.dispatch (this);
+  depth_left++;
+}
+
+void PaintColrLayers::paint_glyph (hb_paint_context_t *c) const
+{
+  const LayerList &paint_offset_lists = c->get_colr_table ()->get_layerList ();
+  for (unsigned i = firstLayerIndex; i < firstLayerIndex + numLayers; i++)
+  {
+    const Paint &paint = paint_offset_lists.get_paint (i);
+    c->funcs->push_group (c->data);
+    c->recurse (paint);
+    c->funcs->pop_group (c->data, HB_PAINT_COMPOSITE_MODE_SRC_OVER);
+  }
+}
+
+void PaintColrGlyph::paint_glyph (hb_paint_context_t *c) const
+{
+  const COLR *colr_table = c->get_colr_table ();
+  const Paint *paint = colr_table->get_base_glyph_paint (gid);
 
+  hb_glyph_extents_t extents = {0};
+  bool has_clip_box = colr_table->get_clip (gid, &extents, c->instancer);
+
+  if (has_clip_box)
+    c->funcs->push_clip_rectangle (c->data,
+				   extents.x_bearing,
+				   extents.y_bearing + extents.height,
+				   extents.x_bearing + extents.width,
+				   extents.y_bearing);
+
+  if (paint)
+    c->recurse (*paint);
+
+  if (has_clip_box)
+    c->funcs->pop_clip (c->data);
+}
+
+} /* namespace OT */
 
-#endif /* HB_OT_COLOR_COLR_TABLE_HH */
+#endif /* OT_COLOR_COLR_COLR_HH */

+ 5 - 6
thirdparty/harfbuzz/src/hb-ot-color-colrv1-closure.hh → thirdparty/harfbuzz/src/OT/Color/COLR/colrv1-closure.hh

@@ -24,12 +24,11 @@
  *
  */
 
-#ifndef HB_OT_COLR_COLRV1_CLOSURE_HH
-#define HB_OT_COLR_COLRV1_CLOSURE_HH
+#ifndef OT_COLOR_COLR_COLRV1_CLOSURE_HH
+#define OT_COLOR_COLR_COLRV1_CLOSURE_HH
 
-#include "hb-open-type.hh"
-#include "hb-ot-layout-common.hh"
-#include "hb-ot-color-colr-table.hh"
+#include "../../../hb-open-type.hh"
+#include "COLR.hh"
 
 /*
  * COLR -- Color
@@ -105,4 +104,4 @@ HB_INTERNAL void PaintComposite::closurev1 (hb_colrv1_closure_context_t* c) cons
 } /* namespace OT */
 
 
-#endif /* HB_OT_COLR_COLRV1_CLOSURE_HH */
+#endif /* OT_COLOR_COLR_COLRV1_CLOSURE_HH */

+ 7 - 7
thirdparty/harfbuzz/src/hb-ot-color-cpal-table.hh → thirdparty/harfbuzz/src/OT/Color/CPAL/CPAL.hh

@@ -25,12 +25,12 @@
  * Google Author(s): Sascha Brawer
  */
 
-#ifndef HB_OT_COLOR_CPAL_TABLE_HH
-#define HB_OT_COLOR_CPAL_TABLE_HH
+#ifndef OT_COLOR_CPAL_CPAL_HH
+#define OT_COLOR_CPAL_CPAL_HH
 
-#include "hb-open-type.hh"
-#include "hb-ot-color.h"
-#include "hb-ot-name.h"
+#include "../../../hb-open-type.hh"
+#include "../../../hb-ot-color.h"
+#include "../../../hb-ot-name.h"
 
 
 /*
@@ -239,7 +239,7 @@ struct CPAL
     TRACE_SUBSET (this);
     if (!numPalettes) return_trace (false);
 
-    const hb_map_t *color_index_map = c->plan->colr_palettes;
+    const hb_map_t *color_index_map = &c->plan->colr_palettes;
     if (color_index_map->is_empty ()) return_trace (false);
 
     hb_set_t retained_color_indices;
@@ -319,4 +319,4 @@ struct CPAL
 } /* namespace OT */
 
 
-#endif /* HB_OT_COLOR_CPAL_TABLE_HH */
+#endif /* OT_COLOR_CPAL_CPAL_HH */

+ 49 - 20
thirdparty/harfbuzz/src/hb-ot-color-sbix-table.hh → thirdparty/harfbuzz/src/OT/Color/sbix/sbix.hh

@@ -25,11 +25,11 @@
  * Google Author(s): Calder Kitagawa
  */
 
-#ifndef HB_OT_COLOR_SBIX_TABLE_HH
-#define HB_OT_COLOR_SBIX_TABLE_HH
+#ifndef OT_COLOR_SBIX_SBIX_HH
+#define OT_COLOR_SBIX_SBIX_HH
 
-#include "hb-open-type.hh"
-#include "hb-ot-layout-common.hh"
+#include "../../../hb-open-type.hh"
+#include "../../../hb-paint.hh"
 
 /*
  * sbix -- Standard Bitmap Graphics
@@ -213,10 +213,11 @@ struct sbix
 
     bool get_extents (hb_font_t          *font,
 		      hb_codepoint_t      glyph,
-		      hb_glyph_extents_t *extents) const
+		      hb_glyph_extents_t *extents,
+		      bool                scale = true) const
     {
       /* We only support PNG right now, and following function checks type. */
-      return get_png_extents (font, glyph, extents);
+      return get_png_extents (font, glyph, extents, scale);
     }
 
     hb_blob_t *reference_png (hb_font_t      *font,
@@ -231,6 +232,37 @@ struct sbix
 						  num_glyphs, available_ppem);
     }
 
+    bool paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data) const
+    {
+      if (!has_data ())
+        return false;
+
+      int x_offset = 0, y_offset = 0;
+      unsigned int strike_ppem = 0;
+      hb_blob_t *blob = reference_png (font, glyph, &x_offset, &y_offset, &strike_ppem);
+      hb_glyph_extents_t extents;
+      hb_glyph_extents_t pixel_extents;
+
+      if (blob == hb_blob_get_empty ())
+        return false;
+
+      if (!hb_font_get_glyph_extents (font, glyph, &extents))
+        return false;
+
+      if (unlikely (!get_extents (font, glyph, &pixel_extents, false)))
+        return false;
+
+      bool ret = funcs->image (data,
+			       blob,
+			       pixel_extents.width, -pixel_extents.height,
+			       HB_PAINT_IMAGE_FORMAT_PNG,
+			       font->slant_xy,
+			       &extents);
+
+      hb_blob_destroy (blob);
+      return ret;
+    }
+
     private:
 
     const SBIXStrike &choose_strike (hb_font_t *font) const
@@ -285,7 +317,8 @@ struct sbix
 
     bool get_png_extents (hb_font_t          *font,
 			  hb_codepoint_t      glyph,
-			  hb_glyph_extents_t *extents) const
+			  hb_glyph_extents_t *extents,
+			  bool                scale = true) const
     {
       /* Following code is safe to call even without data.
        * But faster to short-circuit. */
@@ -310,22 +343,18 @@ struct sbix
       extents->height    = -1 * png.IHDR.height;
 
       /* Convert to font units. */
-      if (strike_ppem)
+      if (strike_ppem && scale)
       {
 	float scale = font->face->get_upem () / (float) strike_ppem;
-	extents->x_bearing = font->em_scalef_x (extents->x_bearing * scale);
-	extents->y_bearing = font->em_scalef_y (extents->y_bearing * scale);
-	extents->width = font->em_scalef_x (extents->width * scale);
-	extents->height = font->em_scalef_y (extents->height * scale);
-      }
-      else
-      {
-	extents->x_bearing = font->em_scale_x (extents->x_bearing);
-	extents->y_bearing = font->em_scale_y (extents->y_bearing);
-	extents->width = font->em_scale_x (extents->width);
-	extents->height = font->em_scale_y (extents->height);
+	extents->x_bearing = roundf (extents->x_bearing * scale);
+	extents->y_bearing = roundf (extents->y_bearing * scale);
+	extents->width = roundf (extents->width * scale);
+	extents->height = roundf (extents->height * scale);
       }
 
+      if (scale)
+	font->scale_glyph_extents (extents);
+
       hb_blob_destroy (blob);
 
       return strike_ppem;
@@ -420,4 +449,4 @@ struct sbix_accelerator_t : sbix::accelerator_t {
 
 } /* namespace OT */
 
-#endif /* HB_OT_COLOR_SBIX_TABLE_HH */
+#endif /* OT_COLOR_SBIX_SBIX_HH */

+ 29 - 4
thirdparty/harfbuzz/src/hb-ot-color-svg-table.hh → thirdparty/harfbuzz/src/OT/Color/svg/svg.hh

@@ -22,10 +22,12 @@
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  */
 
-#ifndef HB_OT_COLOR_SVG_TABLE_HH
-#define HB_OT_COLOR_SVG_TABLE_HH
+#ifndef OT_COLOR_SVG_SVG_HH
+#define OT_COLOR_SVG_SVG_HH
 
-#include "hb-open-type.hh"
+#include "../../../hb-open-type.hh"
+#include "../../../hb-blob.hh"
+#include "../../../hb-paint.hh"
 
 /*
  * SVG -- SVG (Scalable Vector Graphics)
@@ -91,8 +93,31 @@ struct SVG
 
     bool has_data () const { return table->has_data (); }
 
+    bool paint_glyph (hb_font_t *font HB_UNUSED, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data) const
+    {
+      if (!has_data ())
+        return false;
+
+      hb_blob_t *blob = reference_blob_for_glyph (glyph);
+
+      if (blob == hb_blob_get_empty ())
+        return false;
+
+      funcs->image (data,
+		    blob,
+		    0, 0,
+		    HB_PAINT_IMAGE_FORMAT_SVG,
+		    font->slant_xy,
+		    nullptr);
+
+      hb_blob_destroy (blob);
+      return true;
+    }
+
     private:
     hb_blob_ptr_t<SVG> table;
+    public:
+    DEFINE_SIZE_STATIC (sizeof (hb_blob_ptr_t<SVG>));
   };
 
   const SVGDocumentIndexEntry &get_glyph_entry (hb_codepoint_t glyph_id) const
@@ -123,4 +148,4 @@ struct SVG_accelerator_t : SVG::accelerator_t {
 } /* namespace OT */
 
 
-#endif /* HB_OT_COLOR_SVG_TABLE_HH */
+#endif /* OT_COLOR_SVG_SVG_HH */

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

@@ -147,6 +147,7 @@ struct Coverage
     TRACE_SUBSET (this);
     auto it =
     + iter ()
+    | hb_take (c->plan->source->get_num_glyphs ())
     | hb_filter (c->plan->glyph_map_gsub)
     | hb_map_retains_sorting (c->plan->glyph_map_gsub)
     ;

+ 918 - 0
thirdparty/harfbuzz/src/OT/Layout/GDEF/GDEF.hh

@@ -0,0 +1,918 @@
+/*
+ * Copyright © 2007,2008,2009  Red Hat, Inc.
+ * Copyright © 2010,2011,2012  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef OT_LAYOUT_GDEF_GDEF_HH
+#define OT_LAYOUT_GDEF_GDEF_HH
+
+#include "../../../hb-ot-layout-common.hh"
+
+#include "../../../hb-font.hh"
+
+
+namespace OT {
+
+
+/*
+ * Attachment List Table
+ */
+
+/* Array of contour point indices--in increasing numerical order */
+struct AttachPoint : Array16Of<HBUINT16>
+{
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    auto *out = c->serializer->start_embed (*this);
+    if (unlikely (!out)) return_trace (false);
+
+    return_trace (out->serialize (c->serializer, + iter ()));
+  }
+};
+
+struct AttachList
+{
+  unsigned int get_attach_points (hb_codepoint_t glyph_id,
+				  unsigned int start_offset,
+				  unsigned int *point_count /* IN/OUT */,
+				  unsigned int *point_array /* OUT */) const
+  {
+    unsigned int index = (this+coverage).get_coverage (glyph_id);
+    if (index == NOT_COVERED)
+    {
+      if (point_count)
+	*point_count = 0;
+      return 0;
+    }
+
+    const AttachPoint &points = this+attachPoint[index];
+
+    if (point_count)
+    {
+      + points.as_array ().sub_array (start_offset, point_count)
+      | hb_sink (hb_array (point_array, *point_count))
+      ;
+    }
+
+    return points.len;
+  }
+
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
+    const hb_map_t &glyph_map = *c->plan->glyph_map;
+
+    auto *out = c->serializer->start_embed (*this);
+    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+
+    hb_sorted_vector_t<hb_codepoint_t> new_coverage;
+    + hb_zip (this+coverage, attachPoint)
+    | hb_filter (glyphset, hb_first)
+    | hb_filter (subset_offset_array (c, out->attachPoint, this), hb_second)
+    | hb_map (hb_first)
+    | hb_map (glyph_map)
+    | hb_sink (new_coverage)
+    ;
+    out->coverage.serialize_serialize (c->serializer, new_coverage.iter ());
+    return_trace (bool (new_coverage));
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (coverage.sanitize (c, this) && attachPoint.sanitize (c, this));
+  }
+
+  protected:
+  Offset16To<Coverage>
+		coverage;		/* Offset to Coverage table -- from
+					 * beginning of AttachList table */
+  Array16OfOffset16To<AttachPoint>
+		attachPoint;		/* Array of AttachPoint tables
+					 * in Coverage Index order */
+  public:
+  DEFINE_SIZE_ARRAY (4, attachPoint);
+};
+
+/*
+ * Ligature Caret Table
+ */
+
+struct CaretValueFormat1
+{
+  friend struct CaretValue;
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    auto *out = c->serializer->embed (this);
+    if (unlikely (!out)) return_trace (false);
+    return_trace (true);
+  }
+
+  private:
+  hb_position_t get_caret_value (hb_font_t *font, hb_direction_t direction) const
+  {
+    return HB_DIRECTION_IS_HORIZONTAL (direction) ? font->em_scale_x (coordinate) : font->em_scale_y (coordinate);
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  protected:
+  HBUINT16	caretValueFormat;	/* Format identifier--format = 1 */
+  FWORD		coordinate;		/* X or Y value, in design units */
+  public:
+  DEFINE_SIZE_STATIC (4);
+};
+
+struct CaretValueFormat2
+{
+  friend struct CaretValue;
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    auto *out = c->serializer->embed (this);
+    if (unlikely (!out)) return_trace (false);
+    return_trace (true);
+  }
+
+  private:
+  hb_position_t get_caret_value (hb_font_t *font, hb_direction_t direction, hb_codepoint_t glyph_id) const
+  {
+    hb_position_t x, y;
+    font->get_glyph_contour_point_for_origin (glyph_id, caretValuePoint, direction, &x, &y);
+    return HB_DIRECTION_IS_HORIZONTAL (direction) ? x : y;
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  protected:
+  HBUINT16	caretValueFormat;	/* Format identifier--format = 2 */
+  HBUINT16	caretValuePoint;	/* Contour point index on glyph */
+  public:
+  DEFINE_SIZE_STATIC (4);
+};
+
+struct CaretValueFormat3
+{
+  friend struct CaretValue;
+
+  hb_position_t get_caret_value (hb_font_t *font, hb_direction_t direction,
+				 const VariationStore &var_store) const
+  {
+    return HB_DIRECTION_IS_HORIZONTAL (direction) ?
+	   font->em_scale_x (coordinate) + (this+deviceTable).get_x_delta (font, var_store) :
+	   font->em_scale_y (coordinate) + (this+deviceTable).get_y_delta (font, var_store);
+  }
+
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    auto *out = c->serializer->start_embed (*this);
+    if (unlikely (!out)) return_trace (false);
+    if (!c->serializer->embed (caretValueFormat)) return_trace (false);
+    if (!c->serializer->embed (coordinate)) return_trace (false);
+
+    unsigned varidx = (this+deviceTable).get_variation_index ();
+    if (c->plan->layout_variation_idx_delta_map.has (varidx))
+    {
+      int delta = hb_second (c->plan->layout_variation_idx_delta_map.get (varidx));
+      if (delta != 0)
+      {
+        if (!c->serializer->check_assign (out->coordinate, coordinate + delta, HB_SERIALIZE_ERROR_INT_OVERFLOW))
+          return_trace (false);
+      }
+    }
+
+    if (c->plan->all_axes_pinned)
+      return_trace (c->serializer->check_assign (out->caretValueFormat, 1, HB_SERIALIZE_ERROR_INT_OVERFLOW));
+
+    if (!c->serializer->embed (deviceTable))
+      return_trace (false);
+
+    return_trace (out->deviceTable.serialize_copy (c->serializer, deviceTable, this, c->serializer->to_bias (out),
+						   hb_serialize_context_t::Head, &c->plan->layout_variation_idx_delta_map));
+  }
+
+  void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
+  { (this+deviceTable).collect_variation_indices (c); }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) && deviceTable.sanitize (c, this));
+  }
+
+  protected:
+  HBUINT16	caretValueFormat;	/* Format identifier--format = 3 */
+  FWORD		coordinate;		/* X or Y value, in design units */
+  Offset16To<Device>
+		deviceTable;		/* Offset to Device table for X or Y
+					 * value--from beginning of CaretValue
+					 * table */
+  public:
+  DEFINE_SIZE_STATIC (6);
+};
+
+struct CaretValue
+{
+  hb_position_t get_caret_value (hb_font_t *font,
+				 hb_direction_t direction,
+				 hb_codepoint_t glyph_id,
+				 const VariationStore &var_store) const
+  {
+    switch (u.format) {
+    case 1: return u.format1.get_caret_value (font, direction);
+    case 2: return u.format2.get_caret_value (font, direction, glyph_id);
+    case 3: return u.format3.get_caret_value (font, direction, var_store);
+    default:return 0;
+    }
+  }
+
+  template <typename context_t, typename ...Ts>
+  typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
+  {
+    if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value ();
+    TRACE_DISPATCH (this, u.format);
+    switch (u.format) {
+    case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
+    case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...));
+    case 3: return_trace (c->dispatch (u.format3, std::forward<Ts> (ds)...));
+    default:return_trace (c->default_return_value ());
+    }
+  }
+
+  void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
+  {
+    switch (u.format) {
+    case 1:
+    case 2:
+      return;
+    case 3:
+      u.format3.collect_variation_indices (c);
+      return;
+    default: return;
+    }
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    if (!u.format.sanitize (c)) return_trace (false);
+    switch (u.format) {
+    case 1: return_trace (u.format1.sanitize (c));
+    case 2: return_trace (u.format2.sanitize (c));
+    case 3: return_trace (u.format3.sanitize (c));
+    default:return_trace (true);
+    }
+  }
+
+  protected:
+  union {
+  HBUINT16		format;		/* Format identifier */
+  CaretValueFormat1	format1;
+  CaretValueFormat2	format2;
+  CaretValueFormat3	format3;
+  } u;
+  public:
+  DEFINE_SIZE_UNION (2, format);
+};
+
+struct LigGlyph
+{
+  unsigned get_lig_carets (hb_font_t            *font,
+			   hb_direction_t        direction,
+			   hb_codepoint_t        glyph_id,
+			   const VariationStore &var_store,
+			   unsigned              start_offset,
+			   unsigned             *caret_count /* IN/OUT */,
+			   hb_position_t        *caret_array /* OUT */) const
+  {
+    if (caret_count)
+    {
+      + carets.as_array ().sub_array (start_offset, caret_count)
+      | hb_map (hb_add (this))
+      | hb_map ([&] (const CaretValue &value) { return value.get_caret_value (font, direction, glyph_id, var_store); })
+      | hb_sink (hb_array (caret_array, *caret_count))
+      ;
+    }
+
+    return carets.len;
+  }
+
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    auto *out = c->serializer->start_embed (*this);
+    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+
+    + hb_iter (carets)
+    | hb_apply (subset_offset_array (c, out->carets, this))
+    ;
+
+    return_trace (bool (out->carets));
+  }
+
+  void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
+  {
+    for (const Offset16To<CaretValue>& offset : carets.iter ())
+      (this+offset).collect_variation_indices (c);
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (carets.sanitize (c, this));
+  }
+
+  protected:
+  Array16OfOffset16To<CaretValue>
+		carets;			/* Offset array of CaretValue tables
+					 * --from beginning of LigGlyph table
+					 * --in increasing coordinate order */
+  public:
+  DEFINE_SIZE_ARRAY (2, carets);
+};
+
+struct LigCaretList
+{
+  unsigned int get_lig_carets (hb_font_t *font,
+			       hb_direction_t direction,
+			       hb_codepoint_t glyph_id,
+			       const VariationStore &var_store,
+			       unsigned int start_offset,
+			       unsigned int *caret_count /* IN/OUT */,
+			       hb_position_t *caret_array /* OUT */) const
+  {
+    unsigned int index = (this+coverage).get_coverage (glyph_id);
+    if (index == NOT_COVERED)
+    {
+      if (caret_count)
+	*caret_count = 0;
+      return 0;
+    }
+    const LigGlyph &lig_glyph = this+ligGlyph[index];
+    return lig_glyph.get_lig_carets (font, direction, glyph_id, var_store, start_offset, caret_count, caret_array);
+  }
+
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
+    const hb_map_t &glyph_map = *c->plan->glyph_map;
+
+    auto *out = c->serializer->start_embed (*this);
+    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+
+    hb_sorted_vector_t<hb_codepoint_t> new_coverage;
+    + hb_zip (this+coverage, ligGlyph)
+    | hb_filter (glyphset, hb_first)
+    | hb_filter (subset_offset_array (c, out->ligGlyph, this), hb_second)
+    | hb_map (hb_first)
+    | hb_map (glyph_map)
+    | hb_sink (new_coverage)
+    ;
+    out->coverage.serialize_serialize (c->serializer, new_coverage.iter ());
+    return_trace (bool (new_coverage));
+  }
+
+  void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
+  {
+    + hb_zip (this+coverage, ligGlyph)
+    | hb_filter (c->glyph_set, hb_first)
+    | hb_map (hb_second)
+    | hb_map (hb_add (this))
+    | hb_apply ([c] (const LigGlyph& _) { _.collect_variation_indices (c); })
+    ;
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (coverage.sanitize (c, this) && ligGlyph.sanitize (c, this));
+  }
+
+  protected:
+  Offset16To<Coverage>
+		coverage;		/* Offset to Coverage table--from
+					 * beginning of LigCaretList table */
+  Array16OfOffset16To<LigGlyph>
+		ligGlyph;		/* Array of LigGlyph tables
+					 * in Coverage Index order */
+  public:
+  DEFINE_SIZE_ARRAY (4, ligGlyph);
+};
+
+
+struct MarkGlyphSetsFormat1
+{
+  bool covers (unsigned int set_index, hb_codepoint_t glyph_id) const
+  { return (this+coverage[set_index]).get_coverage (glyph_id) != NOT_COVERED; }
+
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    auto *out = c->serializer->start_embed (*this);
+    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+    out->format = format;
+
+    bool ret = true;
+    for (const Offset32To<Coverage>& offset : coverage.iter ())
+    {
+      auto *o = out->coverage.serialize_append (c->serializer);
+      if (unlikely (!o))
+      {
+	ret = false;
+	break;
+      }
+
+      //not using o->serialize_subset (c, offset, this, out) here because
+      //OTS doesn't allow null offset.
+      //See issue: https://github.com/khaledhosny/ots/issues/172
+      c->serializer->push ();
+      c->dispatch (this+offset);
+      c->serializer->add_link (*o, c->serializer->pop_pack ());
+    }
+
+    return_trace (ret && out->coverage.len);
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (coverage.sanitize (c, this));
+  }
+
+  protected:
+  HBUINT16	format;			/* Format identifier--format = 1 */
+  Array16Of<Offset32To<Coverage>>
+		coverage;		/* Array of long offsets to mark set
+					 * coverage tables */
+  public:
+  DEFINE_SIZE_ARRAY (4, coverage);
+};
+
+struct MarkGlyphSets
+{
+  bool covers (unsigned int set_index, hb_codepoint_t glyph_id) const
+  {
+    switch (u.format) {
+    case 1: return u.format1.covers (set_index, glyph_id);
+    default:return false;
+    }
+  }
+
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    switch (u.format) {
+    case 1: return_trace (u.format1.subset (c));
+    default:return_trace (false);
+    }
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    if (!u.format.sanitize (c)) return_trace (false);
+    switch (u.format) {
+    case 1: return_trace (u.format1.sanitize (c));
+    default:return_trace (true);
+    }
+  }
+
+  protected:
+  union {
+  HBUINT16		format;		/* Format identifier */
+  MarkGlyphSetsFormat1	format1;
+  } u;
+  public:
+  DEFINE_SIZE_UNION (2, format);
+};
+
+
+/*
+ * GDEF -- Glyph Definition
+ * https://docs.microsoft.com/en-us/typography/opentype/spec/gdef
+ */
+
+
+template <typename Types>
+struct GDEFVersion1_2
+{
+  friend struct GDEF;
+
+  protected:
+  FixedVersion<>version;		/* Version of the GDEF table--currently
+					 * 0x00010003u */
+  typename Types::template OffsetTo<ClassDef>
+		glyphClassDef;		/* Offset to class definition table
+					 * for glyph type--from beginning of
+					 * GDEF header (may be Null) */
+  typename Types::template OffsetTo<AttachList>
+		attachList;		/* Offset to list of glyphs with
+					 * attachment points--from beginning
+					 * of GDEF header (may be Null) */
+  typename Types::template OffsetTo<LigCaretList>
+		ligCaretList;		/* Offset to list of positioning points
+					 * for ligature carets--from beginning
+					 * of GDEF header (may be Null) */
+  typename Types::template OffsetTo<ClassDef>
+		markAttachClassDef;	/* Offset to class definition table for
+					 * mark attachment type--from beginning
+					 * of GDEF header (may be Null) */
+  typename Types::template OffsetTo<MarkGlyphSets>
+		markGlyphSetsDef;	/* Offset to the table of mark set
+					 * definitions--from beginning of GDEF
+					 * header (may be NULL).  Introduced
+					 * in version 0x00010002. */
+  Offset32To<VariationStore>
+		varStore;		/* Offset to the table of Item Variation
+					 * Store--from beginning of GDEF
+					 * header (may be NULL).  Introduced
+					 * in version 0x00010003. */
+  public:
+  DEFINE_SIZE_MIN (4 + 4 * Types::size);
+
+  unsigned int get_size () const
+  {
+    return min_size +
+	   (version.to_int () >= 0x00010002u ? markGlyphSetsDef.static_size : 0) +
+	   (version.to_int () >= 0x00010003u ? varStore.static_size : 0);
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (version.sanitize (c) &&
+		  glyphClassDef.sanitize (c, this) &&
+		  attachList.sanitize (c, this) &&
+		  ligCaretList.sanitize (c, this) &&
+		  markAttachClassDef.sanitize (c, this) &&
+		  (version.to_int () < 0x00010002u || markGlyphSetsDef.sanitize (c, this)) &&
+		  (version.to_int () < 0x00010003u || varStore.sanitize (c, this)));
+  }
+
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    auto *out = c->serializer->embed (*this);
+    if (unlikely (!out)) return_trace (false);
+
+    bool subset_glyphclassdef = out->glyphClassDef.serialize_subset (c, glyphClassDef, this, nullptr, false, true);
+    bool subset_attachlist = out->attachList.serialize_subset (c, attachList, this);
+    bool subset_ligcaretlist = out->ligCaretList.serialize_subset (c, ligCaretList, this);
+    bool subset_markattachclassdef = out->markAttachClassDef.serialize_subset (c, markAttachClassDef, this, nullptr, false, true);
+
+    bool subset_markglyphsetsdef = false;
+    if (version.to_int () >= 0x00010002u)
+    {
+      subset_markglyphsetsdef = out->markGlyphSetsDef.serialize_subset (c, markGlyphSetsDef, this);
+    }
+
+    bool subset_varstore = false;
+    if (version.to_int () >= 0x00010003u)
+    {
+      if (c->plan->all_axes_pinned)
+        out->varStore = 0;
+      else
+        subset_varstore = out->varStore.serialize_subset (c, varStore, this, c->plan->gdef_varstore_inner_maps.as_array ());
+    }
+
+    if (subset_varstore)
+    {
+      out->version.minor = 3;
+    } else if (subset_markglyphsetsdef) {
+      out->version.minor = 2;
+    } else  {
+      out->version.minor = 0;
+    }
+
+    return_trace (subset_glyphclassdef || subset_attachlist ||
+		  subset_ligcaretlist || subset_markattachclassdef ||
+		  (out->version.to_int () >= 0x00010002u && subset_markglyphsetsdef) ||
+		  (out->version.to_int () >= 0x00010003u && subset_varstore));
+  }
+};
+
+struct GDEF
+{
+  static constexpr hb_tag_t tableTag = HB_OT_TAG_GDEF;
+
+  enum GlyphClasses {
+    UnclassifiedGlyph	= 0,
+    BaseGlyph		= 1,
+    LigatureGlyph	= 2,
+    MarkGlyph		= 3,
+    ComponentGlyph	= 4
+  };
+
+  unsigned int get_size () const
+  {
+    switch (u.version.major) {
+    case 1: return u.version1.get_size ();
+#ifndef HB_NO_BEYOND_64K
+    case 2: return u.version2.get_size ();
+#endif
+    default: return u.version.static_size;
+    }
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    if (unlikely (!u.version.sanitize (c))) return_trace (false);
+    switch (u.version.major) {
+    case 1: return_trace (u.version1.sanitize (c));
+#ifndef HB_NO_BEYOND_64K
+    case 2: return_trace (u.version2.sanitize (c));
+#endif
+    default: return_trace (true);
+    }
+  }
+
+  bool subset (hb_subset_context_t *c) const
+  {
+    switch (u.version.major) {
+    case 1: return u.version1.subset (c);
+#ifndef HB_NO_BEYOND_64K
+    case 2: return u.version2.subset (c);
+#endif
+    default: return false;
+    }
+  }
+
+  bool has_glyph_classes () const
+  {
+    switch (u.version.major) {
+    case 1: return u.version1.glyphClassDef != 0;
+#ifndef HB_NO_BEYOND_64K
+    case 2: return u.version2.glyphClassDef != 0;
+#endif
+    default: return false;
+    }
+  }
+  const ClassDef &get_glyph_class_def () const
+  {
+    switch (u.version.major) {
+    case 1: return this+u.version1.glyphClassDef;
+#ifndef HB_NO_BEYOND_64K
+    case 2: return this+u.version2.glyphClassDef;
+#endif
+    default: return Null(ClassDef);
+    }
+  }
+  bool has_attach_list () const
+  {
+    switch (u.version.major) {
+    case 1: return u.version1.attachList != 0;
+#ifndef HB_NO_BEYOND_64K
+    case 2: return u.version2.attachList != 0;
+#endif
+    default: return false;
+    }
+  }
+  const AttachList &get_attach_list () const
+  {
+    switch (u.version.major) {
+    case 1: return this+u.version1.attachList;
+#ifndef HB_NO_BEYOND_64K
+    case 2: return this+u.version2.attachList;
+#endif
+    default: return Null(AttachList);
+    }
+  }
+  bool has_lig_carets () const
+  {
+    switch (u.version.major) {
+    case 1: return u.version1.ligCaretList != 0;
+#ifndef HB_NO_BEYOND_64K
+    case 2: return u.version2.ligCaretList != 0;
+#endif
+    default: return false;
+    }
+  }
+  const LigCaretList &get_lig_caret_list () const
+  {
+    switch (u.version.major) {
+    case 1: return this+u.version1.ligCaretList;
+#ifndef HB_NO_BEYOND_64K
+    case 2: return this+u.version2.ligCaretList;
+#endif
+    default: return Null(LigCaretList);
+    }
+  }
+  bool has_mark_attachment_types () const
+  {
+    switch (u.version.major) {
+    case 1: return u.version1.markAttachClassDef != 0;
+#ifndef HB_NO_BEYOND_64K
+    case 2: return u.version2.markAttachClassDef != 0;
+#endif
+    default: return false;
+    }
+  }
+  const ClassDef &get_mark_attach_class_def () const
+  {
+    switch (u.version.major) {
+    case 1: return this+u.version1.markAttachClassDef;
+#ifndef HB_NO_BEYOND_64K
+    case 2: return this+u.version2.markAttachClassDef;
+#endif
+    default: return Null(ClassDef);
+    }
+  }
+  bool has_mark_glyph_sets () const
+  {
+    switch (u.version.major) {
+    case 1: return u.version.to_int () >= 0x00010002u && u.version1.markGlyphSetsDef != 0;
+#ifndef HB_NO_BEYOND_64K
+    case 2: return u.version2.markGlyphSetsDef != 0;
+#endif
+    default: return false;
+    }
+  }
+  const MarkGlyphSets &get_mark_glyph_sets () const
+  {
+    switch (u.version.major) {
+    case 1: return u.version.to_int () >= 0x00010002u ? this+u.version1.markGlyphSetsDef : Null(MarkGlyphSets);
+#ifndef HB_NO_BEYOND_64K
+    case 2: return this+u.version2.markGlyphSetsDef;
+#endif
+    default: return Null(MarkGlyphSets);
+    }
+  }
+  bool has_var_store () const
+  {
+    switch (u.version.major) {
+    case 1: return u.version.to_int () >= 0x00010003u && u.version1.varStore != 0;
+#ifndef HB_NO_BEYOND_64K
+    case 2: return u.version2.varStore != 0;
+#endif
+    default: return false;
+    }
+  }
+  const VariationStore &get_var_store () const
+  {
+    switch (u.version.major) {
+    case 1: return u.version.to_int () >= 0x00010003u ? this+u.version1.varStore : Null(VariationStore);
+#ifndef HB_NO_BEYOND_64K
+    case 2: return this+u.version2.varStore;
+#endif
+    default: return Null(VariationStore);
+    }
+  }
+
+
+  bool has_data () const { return u.version.to_int (); }
+  unsigned int get_glyph_class (hb_codepoint_t glyph) const
+  { return get_glyph_class_def ().get_class (glyph); }
+  void get_glyphs_in_class (unsigned int klass, hb_set_t *glyphs) const
+  { get_glyph_class_def ().collect_class (glyphs, klass); }
+
+  unsigned int get_mark_attachment_type (hb_codepoint_t glyph) const
+  { return get_mark_attach_class_def ().get_class (glyph); }
+
+  unsigned int get_attach_points (hb_codepoint_t glyph_id,
+				  unsigned int start_offset,
+				  unsigned int *point_count /* IN/OUT */,
+				  unsigned int *point_array /* OUT */) const
+  { return get_attach_list ().get_attach_points (glyph_id, start_offset, point_count, point_array); }
+
+  unsigned int get_lig_carets (hb_font_t *font,
+			       hb_direction_t direction,
+			       hb_codepoint_t glyph_id,
+			       unsigned int start_offset,
+			       unsigned int *caret_count /* IN/OUT */,
+			       hb_position_t *caret_array /* OUT */) const
+  { return get_lig_caret_list ().get_lig_carets (font,
+						 direction, glyph_id, get_var_store(),
+						 start_offset, caret_count, caret_array); }
+
+  bool mark_set_covers (unsigned int set_index, hb_codepoint_t glyph_id) const
+  { return get_mark_glyph_sets ().covers (set_index, glyph_id); }
+
+  /* glyph_props is a 16-bit integer where the lower 8-bit have bits representing
+   * glyph class and other bits, and high 8-bit the mark attachment type (if any).
+   * Not to be confused with lookup_props which is very similar. */
+  unsigned int get_glyph_props (hb_codepoint_t glyph) const
+  {
+    unsigned int klass = get_glyph_class (glyph);
+
+    static_assert (((unsigned int) HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH == (unsigned int) LookupFlag::IgnoreBaseGlyphs), "");
+    static_assert (((unsigned int) HB_OT_LAYOUT_GLYPH_PROPS_LIGATURE == (unsigned int) LookupFlag::IgnoreLigatures), "");
+    static_assert (((unsigned int) HB_OT_LAYOUT_GLYPH_PROPS_MARK == (unsigned int) LookupFlag::IgnoreMarks), "");
+
+    switch (klass) {
+    default:			return HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED;
+    case BaseGlyph:		return HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH;
+    case LigatureGlyph:		return HB_OT_LAYOUT_GLYPH_PROPS_LIGATURE;
+    case MarkGlyph:
+	  klass = get_mark_attachment_type (glyph);
+	  return HB_OT_LAYOUT_GLYPH_PROPS_MARK | (klass << 8);
+    }
+  }
+
+  HB_INTERNAL bool is_blocklisted (hb_blob_t *blob,
+				   hb_face_t *face) const;
+
+  struct accelerator_t
+  {
+    accelerator_t (hb_face_t *face)
+    {
+      table = hb_sanitize_context_t ().reference_table<GDEF> (face);
+      if (unlikely (table->is_blocklisted (table.get_blob (), face)))
+      {
+	hb_blob_destroy (table.get_blob ());
+	table = hb_blob_get_empty ();
+      }
+    }
+    ~accelerator_t () { table.destroy (); }
+
+    hb_blob_ptr_t<GDEF> table;
+  };
+
+  void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
+  { get_lig_caret_list ().collect_variation_indices (c); }
+
+  void remap_layout_variation_indices (const hb_set_t *layout_variation_indices,
+				       hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map /* OUT */) const
+  {
+    if (!has_var_store ()) return;
+    if (layout_variation_indices->is_empty ()) return;
+
+    unsigned new_major = 0, new_minor = 0;
+    unsigned last_major = (layout_variation_indices->get_min ()) >> 16;
+    for (unsigned idx : layout_variation_indices->iter ())
+    {
+      uint16_t major = idx >> 16;
+      if (major >= get_var_store ().get_sub_table_count ()) break;
+      if (major != last_major)
+      {
+	new_minor = 0;
+	++new_major;
+      }
+
+      unsigned new_idx = (new_major << 16) + new_minor;
+      if (!layout_variation_idx_delta_map->has (idx))
+        continue;
+      int delta = hb_second (layout_variation_idx_delta_map->get (idx));
+
+      layout_variation_idx_delta_map->set (idx, hb_pair_t<unsigned, int> (new_idx, delta));
+      ++new_minor;
+      last_major = major;
+    }
+  }
+
+  protected:
+  union {
+  FixedVersion<>		version;	/* Version identifier */
+  GDEFVersion1_2<SmallTypes>	version1;
+#ifndef HB_NO_BEYOND_64K
+  GDEFVersion1_2<MediumTypes>	version2;
+#endif
+  } u;
+  public:
+  DEFINE_SIZE_MIN (4);
+};
+
+struct GDEF_accelerator_t : GDEF::accelerator_t {
+  GDEF_accelerator_t (hb_face_t *face) : GDEF::accelerator_t (face) {}
+};
+
+} /* namespace OT */
+
+
+#endif /* OT_LAYOUT_GDEF_GDEF_HH */

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

@@ -51,9 +51,9 @@ struct AnchorFormat3
     if (unlikely (!c->serializer->embed (yCoordinate))) return_trace (false);
 
     unsigned x_varidx = xDeviceTable ? (this+xDeviceTable).get_variation_index () : HB_OT_LAYOUT_NO_VARIATIONS_INDEX;
-    if (c->plan->layout_variation_idx_delta_map->has (x_varidx))
+    if (c->plan->layout_variation_idx_delta_map.has (x_varidx))
     {
-      int delta = hb_second (c->plan->layout_variation_idx_delta_map->get (x_varidx));
+      int delta = hb_second (c->plan->layout_variation_idx_delta_map.get (x_varidx));
       if (delta != 0)
       {
         if (!c->serializer->check_assign (out->xCoordinate, xCoordinate + delta,
@@ -63,9 +63,9 @@ struct AnchorFormat3
     }
 
     unsigned y_varidx = yDeviceTable ? (this+yDeviceTable).get_variation_index () : HB_OT_LAYOUT_NO_VARIATIONS_INDEX;
-    if (c->plan->layout_variation_idx_delta_map->has (y_varidx))
+    if (c->plan->layout_variation_idx_delta_map.has (y_varidx))
     {
-      int delta = hb_second (c->plan->layout_variation_idx_delta_map->get (y_varidx));
+      int delta = hb_second (c->plan->layout_variation_idx_delta_map.get (y_varidx));
       if (delta != 0)
       {
         if (!c->serializer->check_assign (out->yCoordinate, yCoordinate + delta,
@@ -80,8 +80,8 @@ struct AnchorFormat3
     if (!c->serializer->embed (xDeviceTable)) return_trace (false);
     if (!c->serializer->embed (yDeviceTable)) return_trace (false);
 
-    out->xDeviceTable.serialize_copy (c->serializer, xDeviceTable, this, 0, hb_serialize_context_t::Head, c->plan->layout_variation_idx_delta_map);
-    out->yDeviceTable.serialize_copy (c->serializer, yDeviceTable, this, 0, hb_serialize_context_t::Head, c->plan->layout_variation_idx_delta_map);
+    out->xDeviceTable.serialize_copy (c->serializer, xDeviceTable, this, 0, hb_serialize_context_t::Head, &c->plan->layout_variation_idx_delta_map);
+    out->yDeviceTable.serialize_copy (c->serializer, yDeviceTable, this, 0, hb_serialize_context_t::Head, &c->plan->layout_variation_idx_delta_map);
     return_trace (out);
   }
 

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

@@ -19,8 +19,8 @@ struct CursivePos
   template <typename context_t, typename ...Ts>
   typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
   {
+    if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value ();
     TRACE_DISPATCH (this, u.format);
-    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
     switch (u.format) {
     case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
     default:return_trace (c->default_return_value ());

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

@@ -143,7 +143,7 @@ struct CursivePosFormat1
     if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
     {
       c->buffer->message (c->font,
-			  "cursive attaching glyph at %d to glyph at %d",
+			  "cursive attaching glyph at %u to glyph at %u",
 			  i, j);
     }
 
@@ -241,7 +241,7 @@ struct CursivePosFormat1
     if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
     {
       c->buffer->message (c->font,
-			  "cursive attached glyph at %d to glyph at %d",
+			  "cursive attached glyph at %u to glyph at %u",
 			  i, j);
     }
 

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

@@ -42,7 +42,7 @@ struct MarkArray : Array16Of<MarkRecord>        /* Array of MarkRecords--in Cove
     if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
     {
       c->buffer->message (c->font,
-			  "attaching mark glyph at %d to glyph at %d",
+			  "attaching mark glyph at %u to glyph at %u",
 			  c->buffer->idx, glyph_pos);
     }
 
@@ -56,7 +56,7 @@ struct MarkArray : Array16Of<MarkRecord>        /* Array of MarkRecords--in Cove
     if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
     {
       c->buffer->message (c->font,
-			  "attached mark glyph at %d to glyph at %d",
+			  "attached mark glyph at %u to glyph at %u",
 			  c->buffer->idx, glyph_pos);
     }
 

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

@@ -22,8 +22,8 @@ struct MarkBasePos
   template <typename context_t, typename ...Ts>
   typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
   {
+    if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value ();
     TRACE_DISPATCH (this, u.format);
-    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
     switch (u.format) {
     case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
 #ifndef HB_NO_BEYOND_64K

+ 54 - 29
thirdparty/harfbuzz/src/OT/Layout/GPOS/MarkBasePosFormat1.hh

@@ -90,6 +90,25 @@ struct MarkBasePosFormat1_2
 
   const Coverage &get_coverage () const { return this+markCoverage; }
 
+  static inline bool accept (hb_buffer_t *buffer, unsigned idx)
+  {
+    /* We only want to attach to the first of a MultipleSubst sequence.
+     * https://github.com/harfbuzz/harfbuzz/issues/740
+     * Reject others...
+     * ...but stop if we find a mark in the MultipleSubst sequence:
+     * https://github.com/harfbuzz/harfbuzz/issues/1020 */
+    return !_hb_glyph_info_multiplied (&buffer->info[idx]) ||
+	   0 == _hb_glyph_info_get_lig_comp (&buffer->info[idx]) ||
+	   (idx == 0 ||
+	    _hb_glyph_info_is_mark (&buffer->info[idx - 1]) ||
+	    !_hb_glyph_info_multiplied (&buffer->info[idx - 1]) ||
+	    _hb_glyph_info_get_lig_id (&buffer->info[idx]) !=
+	    _hb_glyph_info_get_lig_id (&buffer->info[idx - 1]) ||
+	    _hb_glyph_info_get_lig_comp (&buffer->info[idx]) !=
+	    _hb_glyph_info_get_lig_comp (&buffer->info[idx - 1]) + 1
+	    );
+  }
+
   bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
@@ -97,48 +116,54 @@ struct MarkBasePosFormat1_2
     unsigned int mark_index = (this+markCoverage).get_coverage  (buffer->cur().codepoint);
     if (likely (mark_index == NOT_COVERED)) return_trace (false);
 
-    /* Now we search backwards for a non-mark glyph */
+    /* Now we search backwards for a non-mark glyph.
+     * We don't use skippy_iter.prev() to avoid O(n^2) behavior. */
+
     hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
-    skippy_iter.reset (buffer->idx, 1);
     skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks);
-    do {
-      unsigned unsafe_from;
-      if (!skippy_iter.prev (&unsafe_from))
+
+    if (c->last_base_until > buffer->idx)
+    {
+      c->last_base_until = 0;
+      c->last_base = -1;
+    }
+    unsigned j;
+    for (j = buffer->idx; j > c->last_base_until; j--)
+    {
+      auto match = skippy_iter.match (buffer->info[j - 1]);
+      if (match == skippy_iter.MATCH)
+      {
+        // https://github.com/harfbuzz/harfbuzz/issues/4124
+	if (!accept (buffer, j - 1) &&
+	    NOT_COVERED == (this+baseCoverage).get_coverage  (buffer->info[j - 1].codepoint))
+	  match = skippy_iter.SKIP;
+      }
+      if (match == skippy_iter.MATCH)
       {
-        buffer->unsafe_to_concat_from_outbuffer (unsafe_from, buffer->idx + 1);
-        return_trace (false);
+	c->last_base = (signed) j - 1;
+	break;
       }
+    }
+    c->last_base_until = buffer->idx;
+    if (c->last_base == -1)
+    {
+      buffer->unsafe_to_concat_from_outbuffer (0, buffer->idx + 1);
+      return_trace (false);
+    }
 
-      /* We only want to attach to the first of a MultipleSubst sequence.
-       * https://github.com/harfbuzz/harfbuzz/issues/740
-       * Reject others...
-       * ...but stop if we find a mark in the MultipleSubst sequence:
-       * https://github.com/harfbuzz/harfbuzz/issues/1020 */
-      if (!_hb_glyph_info_multiplied (&buffer->info[skippy_iter.idx]) ||
-          0 == _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx]) ||
-          (skippy_iter.idx == 0 ||
-           _hb_glyph_info_is_mark (&buffer->info[skippy_iter.idx - 1]) ||
-           !_hb_glyph_info_multiplied (&buffer->info[skippy_iter.idx - 1]) ||
-           _hb_glyph_info_get_lig_id (&buffer->info[skippy_iter.idx]) !=
-           _hb_glyph_info_get_lig_id (&buffer->info[skippy_iter.idx - 1]) ||
-           _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx]) !=
-           _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx - 1]) + 1
-           ))
-        break;
-      skippy_iter.reject ();
-    } while (true);
+    unsigned idx = (unsigned) c->last_base;
 
     /* Checking that matched glyph is actually a base glyph by GDEF is too strong; disabled */
-    //if (!_hb_glyph_info_is_base_glyph (&buffer->info[skippy_iter.idx])) { return_trace (false); }
+    //if (!_hb_glyph_info_is_base_glyph (&buffer->info[idx])) { return_trace (false); }
 
-    unsigned int base_index = (this+baseCoverage).get_coverage  (buffer->info[skippy_iter.idx].codepoint);
+    unsigned int base_index = (this+baseCoverage).get_coverage  (buffer->info[idx].codepoint);
     if (base_index == NOT_COVERED)
     {
-      buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1);
+      buffer->unsafe_to_concat_from_outbuffer (idx, buffer->idx + 1);
       return_trace (false);
     }
 
-    return_trace ((this+markArray).apply (c, mark_index, base_index, this+baseArray, classCount, skippy_iter.idx));
+    return_trace ((this+markArray).apply (c, mark_index, base_index, this+baseArray, classCount, idx));
   }
 
   bool subset (hb_subset_context_t *c) const

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

@@ -22,8 +22,8 @@ struct MarkLigPos
   template <typename context_t, typename ...Ts>
   typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
   {
+    if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value ();
     TRACE_DISPATCH (this, u.format);
-    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
     switch (u.format) {
     case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
 #ifndef HB_NO_BEYOND_64K

+ 28 - 11
thirdparty/harfbuzz/src/OT/Layout/GPOS/MarkLigPosFormat1.hh

@@ -100,24 +100,41 @@ struct MarkLigPosFormat1_2
     if (likely (mark_index == NOT_COVERED)) return_trace (false);
 
     /* Now we search backwards for a non-mark glyph */
+
     hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
-    skippy_iter.reset (buffer->idx, 1);
     skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks);
-    unsigned unsafe_from;
-    if (!skippy_iter.prev (&unsafe_from))
+
+    if (c->last_base_until > buffer->idx)
     {
-      buffer->unsafe_to_concat_from_outbuffer (unsafe_from, buffer->idx + 1);
+      c->last_base_until = 0;
+      c->last_base = -1;
+    }
+    unsigned j;
+    for (j = buffer->idx; j > c->last_base_until; j--)
+    {
+      auto match = skippy_iter.match (buffer->info[j - 1]);
+      if (match == skippy_iter.MATCH)
+      {
+	c->last_base = (signed) j - 1;
+	break;
+      }
+    }
+    c->last_base_until = buffer->idx;
+    if (c->last_base == -1)
+    {
+      buffer->unsafe_to_concat_from_outbuffer (0, buffer->idx + 1);
       return_trace (false);
     }
 
+    unsigned idx = (unsigned) c->last_base;
+
     /* Checking that matched glyph is actually a ligature by GDEF is too strong; disabled */
-    //if (!_hb_glyph_info_is_ligature (&buffer->info[skippy_iter.idx])) { return_trace (false); }
+    //if (!_hb_glyph_info_is_ligature (&buffer->info[idx])) { return_trace (false); }
 
-    unsigned int j = skippy_iter.idx;
-    unsigned int lig_index = (this+ligatureCoverage).get_coverage  (buffer->info[j].codepoint);
+    unsigned int lig_index = (this+ligatureCoverage).get_coverage  (buffer->info[idx].codepoint);
     if (lig_index == NOT_COVERED)
     {
-      buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1);
+      buffer->unsafe_to_concat_from_outbuffer (idx, buffer->idx + 1);
       return_trace (false);
     }
 
@@ -128,7 +145,7 @@ struct MarkLigPosFormat1_2
     unsigned int comp_count = lig_attach.rows;
     if (unlikely (!comp_count))
     {
-      buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1);
+      buffer->unsafe_to_concat_from_outbuffer (idx, buffer->idx + 1);
       return_trace (false);
     }
 
@@ -137,7 +154,7 @@ struct MarkLigPosFormat1_2
      * can directly use the component index.  If not, we attach the mark
      * glyph to the last component of the ligature. */
     unsigned int comp_index;
-    unsigned int lig_id = _hb_glyph_info_get_lig_id (&buffer->info[j]);
+    unsigned int lig_id = _hb_glyph_info_get_lig_id (&buffer->info[idx]);
     unsigned int mark_id = _hb_glyph_info_get_lig_id (&buffer->cur());
     unsigned int mark_comp = _hb_glyph_info_get_lig_comp (&buffer->cur());
     if (lig_id && lig_id == mark_id && mark_comp > 0)
@@ -145,7 +162,7 @@ struct MarkLigPosFormat1_2
     else
       comp_index = comp_count - 1;
 
-    return_trace ((this+markArray).apply (c, mark_index, comp_index, lig_attach, classCount, j));
+    return_trace ((this+markArray).apply (c, mark_index, comp_index, lig_attach, classCount, idx));
   }
 
   bool subset (hb_subset_context_t *c) const

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

@@ -22,8 +22,8 @@ struct MarkMarkPos
   template <typename context_t, typename ...Ts>
   typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
   {
+    if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value ();
     TRACE_DISPATCH (this, u.format);
-    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
     switch (u.format) {
     case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
 #ifndef HB_NO_BEYOND_64K

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

@@ -25,8 +25,8 @@ struct PairPos
   template <typename context_t, typename ...Ts>
   typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
   {
+    if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value ();
     TRACE_DISPATCH (this, u.format);
-    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
     switch (u.format) {
     case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
     case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...));

+ 2 - 4
thirdparty/harfbuzz/src/OT/Layout/GPOS/PairPosFormat1.hh

@@ -43,7 +43,7 @@ struct PairPosFormat1_3
     {
       valueFormat,
       len1,
-      1 + len1 + len2
+      PairSet::get_size (len1, len2)
     };
 
     return_trace (coverage.sanitize (c, this) && pairSet.sanitize (c, this, &closure));
@@ -177,9 +177,7 @@ struct PairPosFormat1_3
 
   hb_pair_t<unsigned, unsigned> compute_effective_value_formats (const hb_set_t& glyphset) const
   {
-    unsigned len1 = valueFormat[0].get_len ();
-    unsigned len2 = valueFormat[1].get_len ();
-    unsigned record_size = HBUINT16::static_size + Value::static_size * (len1 + len2);
+    unsigned record_size = PairSet::get_size (valueFormat);
 
     unsigned format1 = 0;
     unsigned format2 = 0;

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

@@ -49,7 +49,7 @@ struct PairPosFormat2_4
 
     unsigned int len1 = valueFormat1.get_len ();
     unsigned int len2 = valueFormat2.get_len ();
-    unsigned int stride = len1 + len2;
+    unsigned int stride = HBUINT16::static_size * (len1 + len2);
     unsigned int record_size = valueFormat1.get_size () + valueFormat2.get_size ();
     unsigned int count = (unsigned int) class1Count * (unsigned int) class2Count;
     return_trace (c->check_range ((const void *) values,
@@ -220,7 +220,7 @@ struct PairPosFormat2_4
     if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
     {
       c->buffer->message (c->font,
-			  "try kerning glyphs at %d,%d",
+			  "try kerning glyphs at %u,%u",
 			  c->buffer->idx, skippy_iter.idx);
     }
 
@@ -231,14 +231,14 @@ struct PairPosFormat2_4
       if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
       {
 	c->buffer->message (c->font,
-			    "kerned glyphs at %d,%d",
+			    "kerned glyphs at %u,%u",
 			    c->buffer->idx, skippy_iter.idx);
       }
 
     if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
     {
       c->buffer->message (c->font,
-			  "tried kerning glyphs at %d,%d",
+			  "tried kerning glyphs at %u,%u",
 			  c->buffer->idx, skippy_iter.idx);
     }
 
@@ -298,8 +298,8 @@ struct PairPosFormat2_4
       for (unsigned class2_idx : + hb_range ((unsigned) class2Count) | hb_filter (klass2_map))
       {
         unsigned idx = (class1_idx * (unsigned) class2Count + class2_idx) * (len1 + len2);
-        valueFormat1.copy_values (c->serializer, out->valueFormat1, this, &values[idx], c->plan->layout_variation_idx_delta_map);
-        valueFormat2.copy_values (c->serializer, out->valueFormat2, this, &values[idx + len1], c->plan->layout_variation_idx_delta_map);
+        valueFormat1.copy_values (c->serializer, out->valueFormat1, this, &values[idx], &c->plan->layout_variation_idx_delta_map);
+        valueFormat2.copy_values (c->serializer, out->valueFormat2, this, &values[idx + len1], &c->plan->layout_variation_idx_delta_map);
       }
     }
 

+ 21 - 17
thirdparty/harfbuzz/src/OT/Layout/GPOS/PairSet.hh

@@ -24,11 +24,22 @@ struct PairSet
   public:
   DEFINE_SIZE_MIN (2);
 
+  static unsigned get_size (unsigned len1, unsigned len2)
+  {
+    return Types::HBGlyphID::static_size + Value::static_size * (len1 + len2);
+  }
+  static unsigned get_size (const ValueFormat valueFormats[2])
+  {
+    unsigned len1 = valueFormats[0].get_len ();
+    unsigned len2 = valueFormats[1].get_len ();
+    return get_size (len1, len2);
+  }
+
   struct sanitize_closure_t
   {
     const ValueFormat *valueFormats;
     unsigned int len1; /* valueFormats[0].get_len() */
-    unsigned int stride; /* 1 + len1 + len2 */
+    unsigned int stride; /* bytes */
   };
 
   bool sanitize (hb_sanitize_context_t *c, const sanitize_closure_t *closure) const
@@ -37,7 +48,6 @@ struct PairSet
     if (!(c->check_struct (this)
        && c->check_range (&firstPairValueRecord,
                           len,
-                          HBUINT16::static_size,
                           closure->stride))) return_trace (false);
 
     unsigned int count = len;
@@ -49,9 +59,7 @@ struct PairSet
   bool intersects (const hb_set_t *glyphs,
                    const ValueFormat *valueFormats) const
   {
-    unsigned int len1 = valueFormats[0].get_len ();
-    unsigned int len2 = valueFormats[1].get_len ();
-    unsigned int record_size = HBUINT16::static_size * (1 + len1 + len2);
+    unsigned record_size = get_size (valueFormats);
 
     const PairValueRecord *record = &firstPairValueRecord;
     unsigned int count = len;
@@ -67,9 +75,7 @@ struct PairSet
   void collect_glyphs (hb_collect_glyphs_context_t *c,
                        const ValueFormat *valueFormats) const
   {
-    unsigned int len1 = valueFormats[0].get_len ();
-    unsigned int len2 = valueFormats[1].get_len ();
-    unsigned int record_size = HBUINT16::static_size * (1 + len1 + len2);
+    unsigned record_size = get_size (valueFormats);
 
     const PairValueRecord *record = &firstPairValueRecord;
     c->input->add_array (&record->secondGlyph, len, record_size);
@@ -78,9 +84,7 @@ struct PairSet
   void collect_variation_indices (hb_collect_variation_indices_context_t *c,
                                   const ValueFormat *valueFormats) const
   {
-    unsigned len1 = valueFormats[0].get_len ();
-    unsigned len2 = valueFormats[1].get_len ();
-    unsigned record_size = HBUINT16::static_size * (1 + len1 + len2);
+    unsigned record_size = get_size (valueFormats);
 
     const PairValueRecord *record = &firstPairValueRecord;
     unsigned count = len;
@@ -101,7 +105,7 @@ struct PairSet
     hb_buffer_t *buffer = c->buffer;
     unsigned int len1 = valueFormats[0].get_len ();
     unsigned int len2 = valueFormats[1].get_len ();
-    unsigned int record_size = HBUINT16::static_size * (1 + len1 + len2);
+    unsigned record_size = get_size (len1, len2);
 
     const PairValueRecord *record = hb_bsearch (buffer->info[pos].codepoint,
                                                 &firstPairValueRecord,
@@ -112,7 +116,7 @@ struct PairSet
       if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
       {
 	c->buffer->message (c->font,
-			    "try kerning glyphs at %d,%d",
+			    "try kerning glyphs at %u,%u",
 			    c->buffer->idx, pos);
       }
 
@@ -123,14 +127,14 @@ struct PairSet
 	if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
 	{
 	  c->buffer->message (c->font,
-			      "kerned glyphs at %d,%d",
+			      "kerned glyphs at %u,%u",
 			      c->buffer->idx, pos);
 	}
 
       if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
       {
 	c->buffer->message (c->font,
-			    "tried kerning glyphs at %d,%d",
+			    "tried kerning glyphs at %u,%u",
 			    c->buffer->idx, pos);
       }
 
@@ -168,7 +172,7 @@ struct PairSet
 
     unsigned len1 = valueFormats[0].get_len ();
     unsigned len2 = valueFormats[1].get_len ();
-    unsigned record_size = HBUINT16::static_size + Value::static_size * (len1 + len2);
+    unsigned record_size = get_size (len1, len2);
 
     typename PairValueRecord::context_t context =
     {
@@ -177,7 +181,7 @@ struct PairSet
       newFormats,
       len1,
       &glyph_map,
-      c->plan->layout_variation_idx_delta_map
+      &c->plan->layout_variation_idx_delta_map
     };
 
     const PairValueRecord *record = &firstPairValueRecord;

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

@@ -72,8 +72,8 @@ struct SinglePos
   template <typename context_t, typename ...Ts>
   typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
   {
+    if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value ();
     TRACE_DISPATCH (this, u.format);
-    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
     switch (u.format) {
     case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
     case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...));

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

@@ -28,7 +28,15 @@ struct SinglePosFormat1
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
                   coverage.sanitize (c, this) &&
+                  /* The coverage  table may use a range to represent a set
+                   * of glyphs, which means a small number of bytes can
+                   * generate a large glyph set. Manually modify the
+                   * sanitizer max ops to take this into account.
+                   *
+                   * Note: This check *must* be right after coverage sanitize. */
+                  c->check_ops ((this + coverage).get_population () >> 1) &&
                   valueFormat.sanitize_value (c, this, values));
+
   }
 
   bool intersects (const hb_set_t *glyphs) const
@@ -63,7 +71,7 @@ struct SinglePosFormat1
     if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
     {
       c->buffer->message (c->font,
-			  "positioning glyph at %d",
+			  "positioning glyph at %u",
 			  c->buffer->idx);
     }
 
@@ -72,7 +80,7 @@ struct SinglePosFormat1
     if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
     {
       c->buffer->message (c->font,
-			  "positioned glyph at %d",
+			  "positioned glyph at %u",
 			  c->buffer->idx);
     }
 
@@ -144,7 +152,7 @@ struct SinglePosFormat1
     ;
 
     bool ret = bool (it);
-    SinglePos_serialize (c->serializer, this, it, c->plan->layout_variation_idx_delta_map, c->plan->all_axes_pinned);
+    SinglePos_serialize (c->serializer, this, it, &c->plan->layout_variation_idx_delta_map, c->plan->all_axes_pinned);
     return_trace (ret);
   }
 };

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

@@ -73,7 +73,7 @@ struct SinglePosFormat2
     if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
     {
       c->buffer->message (c->font,
-			  "positioning glyph at %d",
+			  "positioning glyph at %u",
 			  c->buffer->idx);
     }
 
@@ -84,7 +84,7 @@ struct SinglePosFormat2
     if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
     {
       c->buffer->message (c->font,
-			  "positioned glyph at %d",
+			  "positioned glyph at %u",
 			  c->buffer->idx);
     }
 
@@ -163,7 +163,7 @@ struct SinglePosFormat2
     ;
 
     bool ret = bool (it);
-    SinglePos_serialize (c->serializer, this, it, c->plan->layout_variation_idx_delta_map, c->plan->all_axes_pinned);
+    SinglePos_serialize (c->serializer, this, it, &c->plan->layout_variation_idx_delta_map, c->plan->all_axes_pinned);
     return_trace (ret);
   }
 };

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

@@ -371,7 +371,7 @@ struct ValueFormat : HBUINT16
     for (unsigned int i = 0; i < count; i++) {
       if (!sanitize_value_devices (c, base, values))
         return_trace (false);
-      values += stride;
+      values = &StructAtOffset<const Value> (values, stride);
     }
 
     return_trace (true);

+ 3 - 3
thirdparty/harfbuzz/src/OT/Layout/GSUB/AlternateSet.hh

@@ -61,7 +61,7 @@ struct AlternateSet
     {
       c->buffer->sync_so_far ();
       c->buffer->message (c->font,
-			  "replacing glyph at %d (alternate substitution)",
+			  "replacing glyph at %u (alternate substitution)",
 			  c->buffer->idx);
     }
 
@@ -70,8 +70,8 @@ struct AlternateSet
     if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
     {
       c->buffer->message (c->font,
-			  "replaced glyph at %d (alternate substitution)",
-			  c->buffer->idx - 1);
+			  "replaced glyph at %u (alternate substitution)",
+			  c->buffer->idx - 1u);
     }
 
     return_trace (true);

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

@@ -23,8 +23,8 @@ struct AlternateSubst
   template <typename context_t, typename ...Ts>
   typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
   {
+    if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value ();
     TRACE_DISPATCH (this, u.format);
-    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
     switch (u.format) {
     case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
 #ifndef HB_NO_BEYOND_64K

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

@@ -69,7 +69,7 @@ struct Ligature
       {
 	c->buffer->sync_so_far ();
 	c->buffer->message (c->font,
-			    "replacing glyph at %d (ligature substitution)",
+			    "replacing glyph at %u (ligature substitution)",
 			    c->buffer->idx);
       }
 
@@ -78,8 +78,8 @@ struct Ligature
       if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
       {
 	c->buffer->message (c->font,
-			    "replaced glyph at %d (ligature substitution)",
-			    c->buffer->idx - 1);
+			    "replaced glyph at %u (ligature substitution)",
+			    c->buffer->idx - 1u);
       }
 
       return_trace (true);
@@ -138,7 +138,7 @@ struct Ligature
     {
       c->buffer->sync_so_far ();
       c->buffer->message (c->font,
-			  "ligated glyph at %d",
+			  "ligated glyph at %u",
 			  pos);
     }
 

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

@@ -23,8 +23,8 @@ struct LigatureSubst
   template <typename context_t, typename ...Ts>
   typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
   {
+    if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value ();
     TRACE_DISPATCH (this, u.format);
-    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
     switch (u.format) {
     case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
 #ifndef HB_NO_BEYOND_64K

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

@@ -24,8 +24,8 @@ struct MultipleSubst
   template <typename context_t, typename ...Ts>
   typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
   {
+    if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value ();
     TRACE_DISPATCH (this, u.format);
-    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
     switch (u.format) {
     case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
 #ifndef HB_NO_BEYOND_64K

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

@@ -20,8 +20,8 @@ struct ReverseChainSingleSubst
   template <typename context_t, typename ...Ts>
   typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
   {
+    if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value ();
     TRACE_DISPATCH (this, u.format);
-    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
     switch (u.format) {
     case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
     default:return_trace (c->default_return_value ());

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

@@ -135,7 +135,7 @@ struct ReverseChainSingleSubstFormat1
       if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
       {
 	c->buffer->message (c->font,
-			    "replacing glyph at %d (reverse chaining substitution)",
+			    "replacing glyph at %u (reverse chaining substitution)",
 			    c->buffer->idx);
       }
 
@@ -144,7 +144,7 @@ struct ReverseChainSingleSubstFormat1
       if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
       {
 	c->buffer->message (c->font,
-			    "replaced glyph at %d (reverse chaining substitution)",
+			    "replaced glyph at %u (reverse chaining substitution)",
 			    c->buffer->idx);
       }
 

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

@@ -44,7 +44,7 @@ struct Sequence
       {
 	c->buffer->sync_so_far ();
 	c->buffer->message (c->font,
-			    "replacing glyph at %d (multiple substitution)",
+			    "replacing glyph at %u (multiple substitution)",
 			    c->buffer->idx);
       }
 
@@ -53,8 +53,8 @@ struct Sequence
       if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
       {
 	c->buffer->message (c->font,
-			    "replaced glyph at %d (multiple subtitution)",
-			    c->buffer->idx - 1);
+			    "replaced glyph at %u (multiple subtitution)",
+			    c->buffer->idx - 1u);
       }
 
       return_trace (true);
@@ -67,7 +67,7 @@ struct Sequence
       {
 	c->buffer->sync_so_far ();
 	c->buffer->message (c->font,
-			    "deleting glyph at %d (multiple substitution)",
+			    "deleting glyph at %u (multiple substitution)",
 			    c->buffer->idx);
       }
 
@@ -77,7 +77,7 @@ struct Sequence
       {
 	c->buffer->sync_so_far ();
 	c->buffer->message (c->font,
-			    "deleted glyph at %d (multiple substitution)",
+			    "deleted glyph at %u (multiple substitution)",
 			    c->buffer->idx);
       }
 
@@ -88,7 +88,7 @@ struct Sequence
     {
       c->buffer->sync_so_far ();
       c->buffer->message (c->font,
-			  "multiplying glyph at %d",
+			  "multiplying glyph at %u",
 			  c->buffer->idx);
     }
 

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

@@ -27,8 +27,8 @@ struct SingleSubst
   template <typename context_t, typename ...Ts>
   typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
   {
+    if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value ();
     TRACE_DISPATCH (this, u.format);
-    if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
     switch (u.format) {
     case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
     case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...));

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

@@ -25,7 +25,15 @@ struct SingleSubstFormat1_3
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (coverage.sanitize (c, this) && deltaGlyphID.sanitize (c));
+    return_trace (c->check_struct (this) &&
+                  coverage.sanitize (c, this) &&
+                  /* The coverage  table may use a range to represent a set
+                   * of glyphs, which means a small number of bytes can
+                   * generate a large glyph set. Manually modify the
+                   * sanitizer max ops to take this into account.
+                   *
+                   * Note: This check *must* be right after coverage sanitize. */
+                  c->check_ops ((this + coverage).get_population () >> 1));
   }
 
   hb_codepoint_t get_mask () const
@@ -103,7 +111,7 @@ struct SingleSubstFormat1_3
     {
       c->buffer->sync_so_far ();
       c->buffer->message (c->font,
-			  "replacing glyph at %d (single substitution)",
+			  "replacing glyph at %u (single substitution)",
 			  c->buffer->idx);
     }
 
@@ -112,8 +120,8 @@ struct SingleSubstFormat1_3
     if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
     {
       c->buffer->message (c->font,
-			  "replaced glyph at %d (single substitution)",
-			  c->buffer->idx - 1);
+			  "replaced glyph at %u (single substitution)",
+			  c->buffer->idx - 1u);
     }
 
     return_trace (true);

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

@@ -87,7 +87,7 @@ struct SingleSubstFormat2_4
     {
       c->buffer->sync_so_far ();
       c->buffer->message (c->font,
-			  "replacing glyph at %d (single substitution)",
+			  "replacing glyph at %u (single substitution)",
 			  c->buffer->idx);
     }
 
@@ -96,8 +96,8 @@ struct SingleSubstFormat2_4
     if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
     {
       c->buffer->message (c->font,
-			  "replaced glyph at %d (single substitution)",
-			  c->buffer->idx - 1);
+			  "replaced glyph at %u (single substitution)",
+			  c->buffer->idx - 1u);
     }
 
     return_trace (true);

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

@@ -18,11 +18,6 @@ struct glyf_accelerator_t;
 namespace glyf_impl {
 
 
-#ifndef HB_GLYF_MAX_POINTS
-#define HB_GLYF_MAX_POINTS 10000
-#endif
-
-
 enum phantom_point_index_t
 {
   PHANTOM_LEFT   = 0,
@@ -85,28 +80,38 @@ struct Glyph
   }
 
   void update_mtx (const hb_subset_plan_t *plan,
-                   int xMin, int yMax,
+                   int xMin, int xMax,
+                   int yMin, int yMax,
                    const contour_point_vector_t &all_points) const
   {
     hb_codepoint_t new_gid = 0;
     if (!plan->new_gid_for_old_gid (gid, &new_gid))
       return;
 
+    if (type != EMPTY)
+    {
+      plan->bounds_width_map.set (new_gid, xMax - xMin);
+      plan->bounds_height_map.set (new_gid, yMax - yMin);
+    }
+
     unsigned len = all_points.length;
     float leftSideX = all_points[len - 4].x;
     float rightSideX = all_points[len - 3].x;
     float topSideY = all_points[len - 2].y;
     float bottomSideY = all_points[len - 1].y;
 
-    int hori_aw = roundf (rightSideX - leftSideX);
+    signed hori_aw = roundf (rightSideX - leftSideX);
     if (hori_aw < 0) hori_aw = 0;
     int lsb = roundf (xMin - leftSideX);
-    plan->hmtx_map->set (new_gid, hb_pair (hori_aw, lsb));
+    plan->hmtx_map.set (new_gid, hb_pair ((unsigned) hori_aw, lsb));
+    //flag value should be computed using non-empty glyphs
+    if (type != EMPTY && lsb != xMin)
+      plan->head_maxp_info.allXMinIsLsb = false;
 
-    int vert_aw = roundf (topSideY - bottomSideY);
+    signed vert_aw = roundf (topSideY - bottomSideY);
     if (vert_aw < 0) vert_aw = 0;
     int tsb = roundf (topSideY - yMax);
-    plan->vmtx_map->set (new_gid, hb_pair (vert_aw, tsb));
+    plan->vmtx_map.set (new_gid, hb_pair ((unsigned) vert_aw, tsb));
   }
 
   bool compile_header_bytes (const hb_subset_plan_t *plan,
@@ -114,7 +119,7 @@ struct Glyph
                              hb_bytes_t &dest_bytes /* OUT */) const
   {
     GlyphHeader *glyph_header = nullptr;
-    if (type != EMPTY && all_points.length > 4)
+    if (!plan->pinned_at_default && type != EMPTY && all_points.length >= 4)
     {
       glyph_header = (GlyphHeader *) hb_calloc (1, GlyphHeader::static_size);
       if (unlikely (!glyph_header)) return false;
@@ -138,18 +143,33 @@ struct Glyph
       yMax = hb_max (yMax, y);
     }
 
-    update_mtx (plan, roundf (xMin), roundf (yMax), all_points);
+    update_mtx (plan, roundf (xMin), roundf (xMax), roundf (yMin), roundf (yMax), all_points);
+ 
+    int rounded_xMin = roundf (xMin);
+    int rounded_xMax = roundf (xMax);
+    int rounded_yMin = roundf (yMin);
+    int rounded_yMax = roundf (yMax);
+
+    if (type != EMPTY)
+    {
+      plan->head_maxp_info.xMin = hb_min (plan->head_maxp_info.xMin, rounded_xMin);
+      plan->head_maxp_info.yMin = hb_min (plan->head_maxp_info.yMin, rounded_yMin);
+      plan->head_maxp_info.xMax = hb_max (plan->head_maxp_info.xMax, rounded_xMax);
+      plan->head_maxp_info.yMax = hb_max (plan->head_maxp_info.yMax, rounded_yMax);
+    }
 
-    /*for empty glyphs: all_points only include phantom points.
-     *just update metrics and then return */
+    /* when pinned at default, no need to compile glyph header
+     * and for empty glyphs: all_points only include phantom points.
+     * just update metrics and then return */
     if (!glyph_header)
       return true;
 
     glyph_header->numberOfContours = header->numberOfContours;
-    glyph_header->xMin = roundf (xMin);
-    glyph_header->yMin = roundf (yMin);
-    glyph_header->xMax = roundf (xMax);
-    glyph_header->yMax = roundf (yMax);
+
+    glyph_header->xMin = rounded_xMin;
+    glyph_header->yMin = rounded_yMin;
+    glyph_header->xMax = rounded_xMax;
+    glyph_header->yMax = rounded_yMax;
 
     dest_bytes = hb_bytes_t ((const char *)glyph_header, GlyphHeader::static_size);
     return true;
@@ -162,34 +182,54 @@ struct Glyph
                                   hb_bytes_t &dest_end /* OUT */)
   {
     contour_point_vector_t all_points, deltas;
-    if (!get_points (font, glyf, all_points, &deltas, false, false))
+    unsigned composite_contours = 0;
+    head_maxp_info_t *head_maxp_info_p = &plan->head_maxp_info;
+    unsigned *composite_contours_p = &composite_contours;
+
+    // don't compute head/maxp values when glyph has no contours(type is EMPTY)
+    // also ignore .notdef glyph when --notdef-outline is not enabled
+    if (type == EMPTY ||
+        (gid == 0 && !(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE)))
+    {
+      head_maxp_info_p = nullptr;
+      composite_contours_p = nullptr;
+    }
+
+    if (!get_points (font, glyf, all_points, &deltas, head_maxp_info_p, composite_contours_p, false, false))
       return false;
 
     // .notdef, set type to empty so we only update metrics and don't compile bytes for
     // it
     if (gid == 0 &&
         !(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE))
+    {
       type = EMPTY;
-
-    switch (type) {
-    case COMPOSITE:
-      if (!CompositeGlyph (*header, bytes).compile_bytes_with_deltas (dest_start,
-                                                                      deltas,
-                                                                      dest_end))
-        return false;
-      break;
-    case SIMPLE:
-      if (!SimpleGlyph (*header, bytes).compile_bytes_with_deltas (all_points,
-                                                                   plan->flags & HB_SUBSET_FLAGS_NO_HINTING,
-                                                                   dest_end))
-        return false;
-      break;
-    default:
-      /* set empty bytes for empty glyph
-       * do not use source glyph's pointers */
       dest_start = hb_bytes_t ();
       dest_end = hb_bytes_t ();
-      break;
+    }
+
+    //dont compile bytes when pinned at default, just recalculate bounds
+    if (!plan->pinned_at_default) {
+      switch (type) {
+      case COMPOSITE:
+        if (!CompositeGlyph (*header, bytes).compile_bytes_with_deltas (dest_start,
+                                                                        deltas,
+                                                                        dest_end))
+          return false;
+        break;
+      case SIMPLE:
+        if (!SimpleGlyph (*header, bytes).compile_bytes_with_deltas (all_points,
+                                                                     plan->flags & HB_SUBSET_FLAGS_NO_HINTING,
+                                                                     dest_end))
+          return false;
+        break;
+      default:
+        /* set empty bytes for empty glyph
+         * do not use source glyph's pointers */
+        dest_start = hb_bytes_t ();
+        dest_end = hb_bytes_t ();
+        break;
+      }
     }
 
     if (!compile_header_bytes (plan, all_points, dest_start))
@@ -208,13 +248,25 @@ struct Glyph
   bool get_points (hb_font_t *font, const accelerator_t &glyf_accelerator,
 		   contour_point_vector_t &all_points /* OUT */,
 		   contour_point_vector_t *deltas = nullptr, /* OUT */
+		   head_maxp_info_t * head_maxp_info = nullptr, /* OUT */
+		   unsigned *composite_contours = nullptr, /* OUT */
 		   bool shift_points_hori = true,
 		   bool use_my_metrics = true,
 		   bool phantom_only = false,
 		   hb_array_t<int> coords = hb_array_t<int> (),
-		   unsigned int depth = 0) const
+		   unsigned int depth = 0,
+		   unsigned *edge_count = nullptr) const
   {
     if (unlikely (depth > HB_MAX_NESTING_LEVEL)) return false;
+    unsigned stack_edge_count = 0;
+    if (!edge_count) edge_count = &stack_edge_count;
+    if (unlikely (*edge_count > HB_GLYF_MAX_EDGE_COUNT)) return false;
+    (*edge_count)++;
+    
+    if (head_maxp_info)
+    {
+      head_maxp_info->maxComponentDepth = hb_max (head_maxp_info->maxComponentDepth, depth);
+    }
 
     if (!coords)
       coords = hb_array (font->coords, font->num_coords);
@@ -226,6 +278,10 @@ struct Glyph
 
     switch (type) {
     case SIMPLE:
+      if (depth == 0 && head_maxp_info)
+        head_maxp_info->maxContours = hb_max (head_maxp_info->maxContours, (unsigned) header->numberOfContours);
+      if (depth > 0 && composite_contours)
+        *composite_contours += (unsigned) header->numberOfContours;
       if (unlikely (!SimpleGlyph (*header, bytes).get_contour_points (points, phantom_only)))
 	return false;
       break;
@@ -301,6 +357,8 @@ struct Glyph
 
     switch (type) {
     case SIMPLE:
+      if (depth == 0 && head_maxp_info)
+        head_maxp_info->maxPoints = hb_max (head_maxp_info->maxPoints, points.length - 4);
       if (!inplace)
 	all_points.extend (points.as_array ());
       break;
@@ -311,17 +369,19 @@ struct Glyph
       for (auto &item : get_composite_iterator ())
       {
         comp_points.reset ();
-
 	if (unlikely (!glyf_accelerator.glyph_for_gid (item.get_gid ())
 				       .get_points (font,
 						    glyf_accelerator,
 						    comp_points,
 						    deltas,
+						    head_maxp_info,
+						    composite_contours,
 						    shift_points_hori,
 						    use_my_metrics,
 						    phantom_only,
 						    coords,
-						    depth + 1)))
+						    depth + 1,
+						    edge_count)))
 	  return false;
 
 	/* Copy phantom points from component if USE_MY_METRICS flag set */
@@ -357,6 +417,13 @@ struct Glyph
 	comp_index++;
       }
 
+      if (head_maxp_info && depth == 0)
+      {
+        if (composite_contours)
+          head_maxp_info->maxCompositeContours = hb_max (head_maxp_info->maxCompositeContours, *composite_contours);
+        head_maxp_info->maxCompositePoints = hb_max (head_maxp_info->maxCompositePoints, all_points.length);
+        head_maxp_info->maxComponentElements = hb_max (head_maxp_info->maxComponentElements, comp_index);
+      }
       all_points.extend (phantoms);
     } break;
 #ifndef HB_NO_VAR_COMPOSITES
@@ -370,7 +437,11 @@ struct Glyph
 
         comp_points.reset ();
 
-	coord_setter_t coord_setter (coords);
+	auto component_coords = coords;
+	if (item.is_reset_unspecified_axes ())
+	  component_coords = hb_array<int> ();
+
+	coord_setter_t coord_setter (component_coords);
 	item.set_variations (coord_setter, record_points);
 
 	if (unlikely (!glyf_accelerator.glyph_for_gid (item.get_gid ())
@@ -378,11 +449,14 @@ struct Glyph
 						    glyf_accelerator,
 						    comp_points,
 						    deltas,
+						    head_maxp_info,
+						    nullptr,
 						    shift_points_hori,
 						    use_my_metrics,
 						    phantom_only,
 						    coord_setter.get_coords (),
-						    depth + 1)))
+						    depth + 1,
+						    edge_count)))
 	  return false;
 
 	/* Apply component transformation */

+ 6 - 4
thirdparty/harfbuzz/src/OT/glyf/GlyphHeader.hh

@@ -21,10 +21,12 @@ struct GlyphHeader
     /* extents->x_bearing = hb_min (glyph_header.xMin, glyph_header.xMax); */
     int lsb = hb_min (xMin, xMax);
     (void) glyf_accelerator.hmtx->get_leading_bearing_without_var_unscaled (gid, &lsb);
-    extents->x_bearing = font->em_scale_x (lsb);
-    extents->y_bearing = font->em_scale_y (hb_max (yMin, yMax));
-    extents->width     = font->em_scale_x (hb_max (xMin, xMax) - hb_min (xMin, xMax));
-    extents->height    = font->em_scale_y (hb_min (yMin, yMax) - hb_max (yMin, yMax));
+    extents->x_bearing = lsb;
+    extents->y_bearing = hb_max (yMin, yMax);
+    extents->width     = hb_max (xMin, xMax) - hb_min (xMin, xMax);
+    extents->height    = hb_min (yMin, yMax) - hb_max (yMin, yMax);
+
+    font->scale_glyph_extents (extents);
 
     return true;
   }

+ 5 - 5
thirdparty/harfbuzz/src/OT/glyf/SimpleGlyph.hh

@@ -20,7 +20,7 @@ struct SimpleGlyph
     FLAG_X_SAME         = 0x10,
     FLAG_Y_SAME         = 0x20,
     FLAG_OVERLAP_SIMPLE = 0x40,
-    FLAG_RESERVED2      = 0x80
+    FLAG_CUBIC          = 0x80
   };
 
   const GlyphHeader &header;
@@ -184,7 +184,7 @@ struct SimpleGlyph
     if (unlikely (!bytes.check_range (&endPtsOfContours[num_contours]))) return false;
     unsigned int num_points = endPtsOfContours[num_contours - 1] + 1;
 
-    points_.alloc (num_points + 4); // Allocate for phantom points, to avoid a possible copy
+    points_.alloc (num_points + 4, true); // Allocate for phantom points, to avoid a possible copy
     if (!points_.resize (num_points)) return false;
     if (phantom_only) return true;
 
@@ -272,9 +272,9 @@ struct SimpleGlyph
     unsigned num_points = all_points.length - 4;
 
     hb_vector_t<uint8_t> flags, x_coords, y_coords;
-    if (unlikely (!flags.alloc (num_points))) return false;
-    if (unlikely (!x_coords.alloc (2*num_points))) return false;
-    if (unlikely (!y_coords.alloc (2*num_points))) return false;
+    if (unlikely (!flags.alloc (num_points, true))) return false;
+    if (unlikely (!x_coords.alloc (2*num_points, true))) return false;
+    if (unlikely (!y_coords.alloc (2*num_points, true))) return false;
 
     uint8_t lastflag = 255, repeat = 0;
     int prev_x = 0, prev_y = 0;

+ 2 - 10
thirdparty/harfbuzz/src/OT/glyf/SubsetGlyph.hh

@@ -21,22 +21,14 @@ struct SubsetGlyph
 
   bool serialize (hb_serialize_context_t *c,
 		  bool use_short_loca,
-		  const hb_subset_plan_t *plan,
-		  hb_font_t *font)
+		  const hb_subset_plan_t *plan)
   {
     TRACE_SERIALIZE (this);
 
-    if (font)
-    {
-      const OT::glyf_accelerator_t &glyf = *font->face->table.glyf;
-      if (!this->compile_bytes_with_deltas (plan, font, glyf))
-        return_trace (false);
-    }
-
     hb_bytes_t dest_glyph = dest_start.copy (c);
     dest_glyph = hb_bytes_t (&dest_glyph, dest_glyph.length + dest_end.copy (c).length);
     unsigned int pad_length = use_short_loca ? padding () : 0;
-    DEBUG_MSG (SUBSET, nullptr, "serialize %d byte glyph, width %d pad %d", dest_glyph.length, dest_glyph.length + pad_length, pad_length);
+    DEBUG_MSG (SUBSET, nullptr, "serialize %u byte glyph, width %u pad %u", dest_glyph.length, dest_glyph.length + pad_length, pad_length);
 
     HBUINT8 pad;
     pad = 0;

+ 16 - 15
thirdparty/harfbuzz/src/OT/glyf/VarCompositeGlyph.hh

@@ -29,6 +29,7 @@ struct VarCompositeGlyphRecord
     HAVE_TCENTER_Y		= 0x0800,
     GID_IS_24			= 0x1000,
     AXES_HAVE_VARIATION		= 0x2000,
+    RESET_UNSPECIFIED_AXES	= 0x4000,
   };
 
   public:
@@ -60,6 +61,7 @@ struct VarCompositeGlyphRecord
   bool has_more () const { return true; }
 
   bool is_use_my_metrics () const { return flags & USE_MY_METRICS; }
+  bool is_reset_unspecified_axes () const { return flags & RESET_UNSPECIFIED_AXES; }
 
   hb_codepoint_t get_gid () const
   {
@@ -165,8 +167,8 @@ struct VarCompositeGlyphRecord
     float translateX = 0.f;
     float translateY = 0.f;
     float rotation = 0.f;
-    float scaleX = 1.f * (1 << 12);
-    float scaleY = 1.f * (1 << 12);
+    float scaleX = 1.f * (1 << 10);
+    float scaleY = 1.f * (1 << 10);
     float skewX = 0.f;
     float skewY = 0.f;
     float tCenterX = 0.f;
@@ -187,7 +189,7 @@ struct VarCompositeGlyphRecord
     if (flags & AXES_HAVE_VARIATION)
     {
       for (unsigned i = 0; i < count; i++)
-	rec_points[i].x = *q++;
+	rec_points[i].x = q++->to_int ();
       rec_points += count;
     }
     else
@@ -197,11 +199,11 @@ struct VarCompositeGlyphRecord
 
     if (flags & HAVE_TRANSLATE_X)	translateX = * (const FWORD *) p++;
     if (flags & HAVE_TRANSLATE_Y)	translateY = * (const FWORD *) p++;
-    if (flags & HAVE_ROTATION)		rotation = * (const F2DOT14 *) p++;
-    if (flags & HAVE_SCALE_X)		scaleX = * (const F4DOT12 *) p++;
-    if (flags & HAVE_SCALE_Y)		scaleY = * (const F4DOT12 *) p++;
-    if (flags & HAVE_SKEW_X)		skewX = * (const F2DOT14 *) p++;
-    if (flags & HAVE_SKEW_Y)		skewY = * (const F2DOT14 *) p++;
+    if (flags & HAVE_ROTATION)		rotation = ((const F4DOT12 *) p++)->to_int ();
+    if (flags & HAVE_SCALE_X)		scaleX = ((const F6DOT10 *) p++)->to_int ();
+    if (flags & HAVE_SCALE_Y)		scaleY = ((const F6DOT10 *) p++)->to_int ();
+    if (flags & HAVE_SKEW_X)		skewX = ((const F4DOT12 *) p++)->to_int ();
+    if (flags & HAVE_SKEW_Y)		skewY = ((const F4DOT12 *) p++)->to_int ();
     if (flags & HAVE_TCENTER_X)		tCenterX = * (const FWORD *) p++;
     if (flags & HAVE_TCENTER_Y)		tCenterY = * (const FWORD *) p++;
 
@@ -270,19 +272,19 @@ struct VarCompositeGlyphRecord
     }
     if (flags & HAVE_ROTATION)
     {
-      rotation = rec_points[0].x / (1 << 14);
+      rotation = rec_points[0].x / (1 << 12);
       rec_points++;
     }
     if (flags & (HAVE_SCALE_X | HAVE_SCALE_Y))
     {
-      scaleX = rec_points[0].x / (1 << 12);
-      scaleY = rec_points[0].y / (1 << 12);
+      scaleX = rec_points[0].x / (1 << 10);
+      scaleY = rec_points[0].y / (1 << 10);
       rec_points++;
     }
     if (flags & (HAVE_SKEW_X | HAVE_SKEW_Y))
     {
-      skewX = rec_points[0].x / (1 << 14);
-      skewY = rec_points[0].y / (1 << 14);
+      skewX = rec_points[0].x / (1 << 12);
+      skewY = rec_points[0].y / (1 << 12);
       rec_points++;
     }
     if (flags & (HAVE_TCENTER_X | HAVE_TCENTER_Y))
@@ -316,9 +318,8 @@ struct VarCompositeGlyphRecord
     {
       unsigned axis_index = axis_width == 1 ? (unsigned) *p++ : (unsigned) *q++;
 
-      signed v = have_variations ? rec_points[i].x : *a++;
+      signed v = have_variations ? rec_points[i].x : a++->to_int ();
 
-      v += setter[axis_index];
       v = hb_clamp (v, -(1<<14), (1<<14));
       setter[axis_index] = v;
     }

+ 16 - 2
thirdparty/harfbuzz/src/OT/glyf/glyf-helpers.hh

@@ -25,7 +25,7 @@ _write_loca (IteratorIn&& it, bool short_offsets, IteratorOut&& dest)
   | hb_map ([=, &offset] (unsigned int padded_size)
 	    {
 	      offset += padded_size;
-	      DEBUG_MSG (SUBSET, nullptr, "loca entry offset %d", offset);
+	      DEBUG_MSG (SUBSET, nullptr, "loca entry offset %u", offset);
 	      return offset >> right_shift;
 	    })
   | hb_sink (dest)
@@ -44,6 +44,20 @@ _add_head_and_set_loca_version (hb_subset_plan_t *plan, bool use_short_loca)
 
   head *head_prime = (head *) hb_blob_get_data_writable (head_prime_blob, nullptr);
   head_prime->indexToLocFormat = use_short_loca ? 0 : 1;
+  if (plan->normalized_coords)
+  {
+    head_prime->xMin = plan->head_maxp_info.xMin;
+    head_prime->xMax = plan->head_maxp_info.xMax;
+    head_prime->yMin = plan->head_maxp_info.yMin;
+    head_prime->yMax = plan->head_maxp_info.yMax;
+
+    unsigned orig_flag = head_prime->flags;
+    if (plan->head_maxp_info.allXMinIsLsb)
+      orig_flag |= 1 << 1;
+    else
+      orig_flag &= ~(1 << 1);
+    head_prime->flags = orig_flag;
+  }
   bool success = plan->add_table (HB_OT_TAG_head, head_prime_blob);
 
   hb_blob_destroy (head_prime_blob);
@@ -61,7 +75,7 @@ _add_loca_and_head (hb_subset_plan_t * plan, Iterator padded_offsets, bool use_s
 
   if (unlikely (!loca_prime_data)) return false;
 
-  DEBUG_MSG (SUBSET, nullptr, "loca entry_size %d num_offsets %d size %d",
+  DEBUG_MSG (SUBSET, nullptr, "loca entry_size %u num_offsets %u size %u",
 	     entry_size, num_offsets, entry_size * num_offsets);
 
   if (use_short_loca)

+ 70 - 44
thirdparty/harfbuzz/src/OT/glyf/glyf.hh

@@ -7,6 +7,7 @@
 #include "../../hb-ot-hmtx-table.hh"
 #include "../../hb-ot-var-gvar-table.hh"
 #include "../../hb-draw.hh"
+#include "../../hb-paint.hh"
 
 #include "glyf-helpers.hh"
 #include "Glyph.hh"
@@ -42,14 +43,13 @@ struct glyf
   bool serialize (hb_serialize_context_t *c,
 		  Iterator it,
                   bool use_short_loca,
-		  const hb_subset_plan_t *plan,
-		  hb_font_t *font)
+		  const hb_subset_plan_t *plan)
   {
     TRACE_SERIALIZE (this);
 
     unsigned init_len = c->length ();
     for (auto &_ : it)
-      if (unlikely (!_.serialize (c, use_short_loca, plan, font)))
+      if (unlikely (!_.serialize (c, use_short_loca, plan)))
         return false;
 
     /* As a special case when all glyph in the font are empty, add a zero byte
@@ -75,59 +75,66 @@ struct glyf
     glyf *glyf_prime = c->serializer->start_embed <glyf> ();
     if (unlikely (!c->serializer->check_success (glyf_prime))) return_trace (false);
 
-    hb_vector_t<glyf_impl::SubsetGlyph> glyphs;
-    _populate_subset_glyphs (c->plan, glyphs);
-
     hb_font_t *font = nullptr;
-    if (!c->plan->pinned_at_default)
+    if (c->plan->normalized_coords)
     {
       font = _create_font_for_instancing (c->plan);
       if (unlikely (!font)) return false;
     }
 
-    auto padded_offsets =
-    + hb_iter (glyphs)
-    | hb_map (&glyf_impl::SubsetGlyph::padded_size)
-    ;
+    hb_vector_t<unsigned> padded_offsets;
+    unsigned num_glyphs = c->plan->num_output_glyphs ();
+    if (unlikely (!padded_offsets.resize (num_glyphs)))
+      return false;
+
+    hb_vector_t<glyf_impl::SubsetGlyph> glyphs;
+    if (!_populate_subset_glyphs (c->plan, font, glyphs))
+      return false;
+
+    if (font)
+      hb_font_destroy (font);
+
+    unsigned max_offset = 0;
+    for (unsigned i = 0; i < num_glyphs; i++)
+    {
+      padded_offsets[i] = glyphs[i].padded_size ();
+      max_offset += padded_offsets[i];
+    }
 
     bool use_short_loca = false;
     if (likely (!c->plan->force_long_loca))
-    {
-      unsigned max_offset = + padded_offsets | hb_reduce (hb_add, 0);
       use_short_loca = max_offset < 0x1FFFF;
-    }
 
-    glyf_prime->serialize (c->serializer, glyphs.writer (), use_short_loca, c->plan, font);
     if (!use_short_loca) {
-      padded_offsets =
-          + hb_iter (glyphs)
-          | hb_map (&glyf_impl::SubsetGlyph::length)
-          ;
+      for (unsigned i = 0; i < num_glyphs; i++)
+        padded_offsets[i] = glyphs[i].length ();
     }
 
-    if (font)
-    {
-      _free_compiled_subset_glyphs (&glyphs);
-      hb_font_destroy (font);
-    }
+    bool result = glyf_prime->serialize (c->serializer, glyphs.writer (), use_short_loca, c->plan);
+    if (c->plan->normalized_coords && !c->plan->pinned_at_default)
+      _free_compiled_subset_glyphs (glyphs, glyphs.length - 1);
+
+    if (!result) return false;
 
     if (unlikely (c->serializer->in_error ())) return_trace (false);
+
     return_trace (c->serializer->check_success (glyf_impl::_add_loca_and_head (c->plan,
-									       padded_offsets,
+									       padded_offsets.iter (),
 									       use_short_loca)));
   }
 
-  void
+  bool
   _populate_subset_glyphs (const hb_subset_plan_t   *plan,
+			   hb_font_t                *font,
 			   hb_vector_t<glyf_impl::SubsetGlyph> &glyphs /* OUT */) const;
 
   hb_font_t *
   _create_font_for_instancing (const hb_subset_plan_t *plan) const;
 
-  void _free_compiled_subset_glyphs (hb_vector_t<glyf_impl::SubsetGlyph> *glyphs) const
+  void _free_compiled_subset_glyphs (hb_vector_t<glyf_impl::SubsetGlyph> &glyphs, unsigned index) const
   {
-    for (auto _ : *glyphs)
-      _.free_compiled_bytes ();
+    for (unsigned i = 0; i <= index && i < glyphs.length; i++)
+      glyphs[i].free_compiled_bytes ();
   }
 
   protected:
@@ -193,7 +200,7 @@ struct glyf_accelerator_t
     contour_point_vector_t all_points;
 
     bool phantom_only = !consumer.is_consuming_contour_points ();
-    if (unlikely (!glyph_for_gid (gid).get_points (font, *this, all_points, nullptr, true, true, phantom_only)))
+    if (unlikely (!glyph_for_gid (gid).get_points (font, *this, all_points, nullptr, nullptr, nullptr, true, true, phantom_only)))
       return false;
 
     if (consumer.is_consuming_contour_points ())
@@ -247,19 +254,14 @@ struct glyf_accelerator_t
 	  extents->y_bearing = 0;
 	  return;
 	}
-	if (scaled)
-	{
-	  extents->x_bearing = font->em_scalef_x (min_x);
-	  extents->width = font->em_scalef_x (max_x) - extents->x_bearing;
-	  extents->y_bearing = font->em_scalef_y (max_y);
-	  extents->height = font->em_scalef_y (min_y) - extents->y_bearing;
-	}
-	else
 	{
 	  extents->x_bearing = roundf (min_x);
 	  extents->width = roundf (max_x - extents->x_bearing);
 	  extents->y_bearing = roundf (max_y);
 	  extents->height = roundf (min_y - extents->y_bearing);
+
+	  if (scaled)
+	    font->scale_glyph_extents (extents);
 	}
       }
 
@@ -337,6 +339,15 @@ struct glyf_accelerator_t
     return glyph_for_gid (gid).get_extents_without_var_scaled (font, *this, extents);
   }
 
+  bool paint_glyph (hb_font_t *font, hb_codepoint_t gid, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const
+  {
+    funcs->push_clip_glyph (data, gid, font);
+    funcs->color (data, true, foreground);
+    funcs->pop_clip (data);
+
+    return true;
+  }
+
   const glyf_impl::Glyph
   glyph_for_gid (hb_codepoint_t gid, bool needs_padding_removal = false) const
   {
@@ -385,14 +396,16 @@ struct glyf_accelerator_t
 };
 
 
-inline void
+inline bool
 glyf::_populate_subset_glyphs (const hb_subset_plan_t   *plan,
+			       hb_font_t *font,
 			       hb_vector_t<glyf_impl::SubsetGlyph>& glyphs /* OUT */) const
 {
   OT::glyf_accelerator_t glyf (plan->source);
   unsigned num_glyphs = plan->num_output_glyphs ();
-  if (!glyphs.resize (num_glyphs)) return;
+  if (!glyphs.resize (num_glyphs)) return false;
 
+  unsigned idx = 0;
   for (auto p : plan->glyph_map->iter ())
   {
     unsigned new_gid = p.second;
@@ -401,7 +414,7 @@ glyf::_populate_subset_glyphs (const hb_subset_plan_t   *plan,
 
     if (unlikely (new_gid == 0 &&
                   !(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE)) &&
-                  plan->pinned_at_default)
+                  !plan->normalized_coords)
       subset_glyph.source_glyph = glyf_impl::Glyph ();
     else
     {
@@ -414,7 +427,20 @@ glyf::_populate_subset_glyphs (const hb_subset_plan_t   *plan,
       subset_glyph.drop_hints_bytes ();
     else
       subset_glyph.dest_start = subset_glyph.source_glyph.get_bytes ();
+
+    if (font)
+    {
+      if (unlikely (!subset_glyph.compile_bytes_with_deltas (plan, font, glyf)))
+      {
+        // when pinned at default, only bounds are updated, thus no need to free
+        if (!plan->pinned_at_default && idx > 0)
+          _free_compiled_subset_glyphs (glyphs, idx - 1);
+        return false;
+      }
+      idx++;
+    }
   }
+  return true;
 }
 
 inline hb_font_t *
@@ -424,10 +450,10 @@ glyf::_create_font_for_instancing (const hb_subset_plan_t *plan) const
   if (unlikely (font == hb_font_get_empty ())) return nullptr;
 
   hb_vector_t<hb_variation_t> vars;
-  if (unlikely (!vars.alloc (plan->user_axes_location->get_population ())))
+  if (unlikely (!vars.alloc (plan->user_axes_location.get_population (), true)))
     return nullptr;
 
-  for (auto _ : *plan->user_axes_location)
+  for (auto _ : plan->user_axes_location)
   {
     hb_variation_t var;
     var.tag = _.first;
@@ -436,7 +462,7 @@ glyf::_create_font_for_instancing (const hb_subset_plan_t *plan) const
   }
 
 #ifndef HB_NO_VAR
-  hb_font_set_variations (font, vars.arrayZ, plan->user_axes_location->get_population ());
+  hb_font_set_variations (font, vars.arrayZ, plan->user_axes_location.get_population ());
 #endif
   return font;
 }

+ 77 - 19
thirdparty/harfbuzz/src/OT/glyf/path-builder.hh

@@ -26,22 +26,29 @@ struct path_builder_t
 
     optional_point_t lerp (optional_point_t p, float t)
     { return optional_point_t (x + t * (p.x - x), y + t * (p.y - y)); }
-  } first_oncurve, first_offcurve, last_offcurve;
+  } first_oncurve, first_offcurve, first_offcurve2, last_offcurve, last_offcurve2;
 
   path_builder_t (hb_font_t *font_, hb_draw_session_t &draw_session_)
   {
     font = font_;
     draw_session = &draw_session_;
-    first_oncurve = first_offcurve = last_offcurve = optional_point_t ();
+    first_oncurve = first_offcurve = first_offcurve2 = last_offcurve = last_offcurve2 = optional_point_t ();
   }
 
   /* based on https://github.com/RazrFalcon/ttf-parser/blob/4f32821/src/glyf.rs#L287
      See also:
      * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM01/Chap1.html
-     * https://stackoverflow.com/a/20772557 */
+     * https://stackoverflow.com/a/20772557
+     *
+     * Cubic support added. */
   void consume_point (const contour_point_t &point)
   {
     bool is_on_curve = point.flag & glyf_impl::SimpleGlyph::FLAG_ON_CURVE;
+#ifdef HB_NO_CUBIC_GLYF
+    bool is_cubic = false;
+#else
+    bool is_cubic = !is_on_curve && (point.flag & glyf_impl::SimpleGlyph::FLAG_CUBIC);
+#endif
     optional_point_t p (font->em_fscalef_x (point.x), font->em_fscalef_y (point.y));
     if (!first_oncurve)
     {
@@ -52,7 +59,12 @@ struct path_builder_t
       }
       else
       {
-	if (first_offcurve)
+	if (is_cubic && !first_offcurve2)
+	{
+	  first_offcurve2 = first_offcurve;
+	  first_offcurve = p;
+	}
+	else if (first_offcurve)
 	{
 	  optional_point_t mid = first_offcurve.lerp (p, .5f);
 	  first_oncurve = mid;
@@ -69,16 +81,41 @@ struct path_builder_t
       {
 	if (is_on_curve)
 	{
-	  draw_session->quadratic_to (last_offcurve.x, last_offcurve.y,
-				     p.x, p.y);
+	  if (last_offcurve2)
+	  {
+	    draw_session->cubic_to (last_offcurve2.x, last_offcurve2.y,
+				    last_offcurve.x, last_offcurve.y,
+				    p.x, p.y);
+	    last_offcurve2 = optional_point_t ();
+	  }
+	  else
+	    draw_session->quadratic_to (last_offcurve.x, last_offcurve.y,
+				       p.x, p.y);
 	  last_offcurve = optional_point_t ();
 	}
 	else
 	{
-	  optional_point_t mid = last_offcurve.lerp (p, .5f);
-	  draw_session->quadratic_to (last_offcurve.x, last_offcurve.y,
-				     mid.x, mid.y);
-	  last_offcurve = p;
+	  if (is_cubic && !last_offcurve2)
+	  {
+	    last_offcurve2 = last_offcurve;
+	    last_offcurve = p;
+	  }
+	  else
+	  {
+	    optional_point_t mid = last_offcurve.lerp (p, .5f);
+
+	    if (is_cubic)
+	    {
+	      draw_session->cubic_to (last_offcurve2.x, last_offcurve2.y,
+				      last_offcurve.x, last_offcurve.y,
+				      mid.x, mid.y);
+	      last_offcurve2 = optional_point_t ();
+	    }
+	    else
+	      draw_session->quadratic_to (last_offcurve.x, last_offcurve.y,
+					 mid.x, mid.y);
+	    last_offcurve = p;
+	  }
 	}
       }
       else
@@ -94,19 +131,40 @@ struct path_builder_t
     {
       if (first_offcurve && last_offcurve)
       {
-	optional_point_t mid = last_offcurve.lerp (first_offcurve, .5f);
-	draw_session->quadratic_to (last_offcurve.x, last_offcurve.y,
-				   mid.x, mid.y);
+	optional_point_t mid = last_offcurve.lerp (first_offcurve2 ?
+						   first_offcurve2 :
+						   first_offcurve, .5f);
+	if (last_offcurve2)
+	  draw_session->cubic_to (last_offcurve2.x, last_offcurve2.y,
+				  last_offcurve.x, last_offcurve.y,
+				  mid.x, mid.y);
+	else
+	  draw_session->quadratic_to (last_offcurve.x, last_offcurve.y,
+				     mid.x, mid.y);
 	last_offcurve = optional_point_t ();
-	/* now check the rest */
       }
+      /* now check the rest */
 
       if (first_offcurve && first_oncurve)
-	draw_session->quadratic_to (first_offcurve.x, first_offcurve.y,
-				   first_oncurve.x, first_oncurve.y);
+      {
+        if (first_offcurve2)
+	  draw_session->cubic_to (first_offcurve2.x, first_offcurve2.y,
+				  first_offcurve.x, first_offcurve.y,
+				  first_oncurve.x, first_oncurve.y);
+	else
+	  draw_session->quadratic_to (first_offcurve.x, first_offcurve.y,
+				     first_oncurve.x, first_oncurve.y);
+      }
       else if (last_offcurve && first_oncurve)
-	draw_session->quadratic_to (last_offcurve.x, last_offcurve.y,
-				   first_oncurve.x, first_oncurve.y);
+      {
+	if (last_offcurve2)
+	  draw_session->cubic_to (last_offcurve2.x, last_offcurve2.y,
+				  last_offcurve.x, last_offcurve.y,
+				  first_oncurve.x, first_oncurve.y);
+	else
+	  draw_session->quadratic_to (last_offcurve.x, last_offcurve.y,
+				     first_oncurve.x, first_oncurve.y);
+      }
       else if (first_oncurve)
 	draw_session->line_to (first_oncurve.x, first_oncurve.y);
       else if (first_offcurve)
@@ -117,7 +175,7 @@ struct path_builder_t
       }
 
       /* Getting ready for the next contour */
-      first_oncurve = first_offcurve = last_offcurve = optional_point_t ();
+      first_oncurve = first_offcurve = last_offcurve = last_offcurve2 = optional_point_t ();
       draw_session->close_path ();
     }
   }

+ 589 - 0
thirdparty/harfbuzz/src/OT/name/name.hh

@@ -0,0 +1,589 @@
+/*
+ * Copyright © 2011,2012  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef OT_NAME_NAME_HH
+#define OT_NAME_NAME_HH
+
+#include "../../hb-open-type.hh"
+#include "../../hb-ot-name-language.hh"
+#include "../../hb-aat-layout.hh"
+#include "../../hb-utf.hh"
+
+
+namespace OT {
+
+template <typename in_utf_t, typename out_utf_t>
+inline unsigned int
+hb_ot_name_convert_utf (hb_bytes_t                       bytes,
+			unsigned int                    *text_size /* IN/OUT */,
+			typename out_utf_t::codepoint_t *text /* OUT */)
+{
+  unsigned int src_len = bytes.length / sizeof (typename in_utf_t::codepoint_t);
+  const typename in_utf_t::codepoint_t *src = (const typename in_utf_t::codepoint_t *) bytes.arrayZ;
+  const typename in_utf_t::codepoint_t *src_end = src + src_len;
+
+  typename out_utf_t::codepoint_t *dst = text;
+
+  hb_codepoint_t unicode;
+  const hb_codepoint_t replacement = HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT;
+
+  if (text_size && *text_size)
+  {
+    (*text_size)--; /* Save room for NUL-termination. */
+    const typename out_utf_t::codepoint_t *dst_end = text + *text_size;
+
+    while (src < src_end && dst < dst_end)
+    {
+      const typename in_utf_t::codepoint_t *src_next = in_utf_t::next (src, src_end, &unicode, replacement);
+      typename out_utf_t::codepoint_t *dst_next = out_utf_t::encode (dst, dst_end, unicode);
+      if (dst_next == dst)
+	break; /* Out-of-room. */
+
+      dst = dst_next;
+      src = src_next;
+    }
+
+    *text_size = dst - text;
+    *dst = 0; /* NUL-terminate. */
+  }
+
+  /* Accumulate length of rest. */
+  unsigned int dst_len = dst - text;
+  while (src < src_end)
+  {
+    src = in_utf_t::next (src, src_end, &unicode, replacement);
+    dst_len += out_utf_t::encode_len (unicode);
+  }
+  return dst_len;
+}
+
+#define entry_score var.u16[0]
+#define entry_index var.u16[1]
+
+
+/*
+ * name -- Naming
+ * https://docs.microsoft.com/en-us/typography/opentype/spec/name
+ */
+#define HB_OT_TAG_name HB_TAG('n','a','m','e')
+
+#define UNSUPPORTED	42
+
+struct NameRecord
+{
+  hb_language_t language (hb_face_t *face) const
+  {
+#ifndef HB_NO_OT_NAME_LANGUAGE
+    unsigned int p = platformID;
+    unsigned int l = languageID;
+
+    if (p == 3)
+      return _hb_ot_name_language_for_ms_code (l);
+
+    if (p == 1)
+      return _hb_ot_name_language_for_mac_code (l);
+
+#ifndef HB_NO_OT_NAME_LANGUAGE_AAT
+    if (p == 0)
+      return face->table.ltag->get_language (l);
+#endif
+
+#endif
+    return HB_LANGUAGE_INVALID;
+  }
+
+  uint16_t score () const
+  {
+    /* Same order as in cmap::find_best_subtable(). */
+    unsigned int p = platformID;
+    unsigned int e = encodingID;
+
+    /* 32-bit. */
+    if (p == 3 && e == 10) return 0;
+    if (p == 0 && e ==  6) return 1;
+    if (p == 0 && e ==  4) return 2;
+
+    /* 16-bit. */
+    if (p == 3 && e ==  1) return 3;
+    if (p == 0 && e ==  3) return 4;
+    if (p == 0 && e ==  2) return 5;
+    if (p == 0 && e ==  1) return 6;
+    if (p == 0 && e ==  0) return 7;
+
+    /* Symbol. */
+    if (p == 3 && e ==  0) return 8;
+
+    /* We treat all Mac Latin names as ASCII only. */
+    if (p == 1 && e ==  0) return 10; /* 10 is magic number :| */
+
+    return UNSUPPORTED;
+  }
+
+  NameRecord* copy (hb_serialize_context_t *c, const void *base
+#ifdef HB_EXPERIMENTAL_API
+                    , const hb_hashmap_t<hb_ot_name_record_ids_t, hb_bytes_t> *name_table_overrides
+#endif
+		    ) const
+  {
+    TRACE_SERIALIZE (this);
+    HB_UNUSED auto snap = c->snapshot ();
+    auto *out = c->embed (this);
+    if (unlikely (!out)) return_trace (nullptr);
+#ifdef HB_EXPERIMENTAL_API
+    hb_ot_name_record_ids_t record_ids (platformID, encodingID, languageID, nameID);
+    hb_bytes_t* name_bytes;
+
+    if (name_table_overrides->has (record_ids, &name_bytes)) {
+      hb_bytes_t encoded_bytes = *name_bytes;
+      char *name_str_utf16_be = nullptr;
+
+      if (platformID != 1)
+      {
+        unsigned text_size = hb_ot_name_convert_utf<hb_utf8_t, hb_utf16_be_t> (*name_bytes, nullptr, nullptr);
+  
+        text_size++; // needs to consider NULL terminator for use in hb_ot_name_convert_utf()
+        unsigned byte_len = text_size * hb_utf16_be_t::codepoint_t::static_size;
+        name_str_utf16_be = (char *) hb_calloc (byte_len, 1);
+        if (!name_str_utf16_be)
+        {
+          c->revert (snap);
+          return_trace (nullptr);
+        }
+        hb_ot_name_convert_utf<hb_utf8_t, hb_utf16_be_t> (*name_bytes, &text_size,
+                                                          (hb_utf16_be_t::codepoint_t *) name_str_utf16_be);
+  
+        unsigned encoded_byte_len = text_size * hb_utf16_be_t::codepoint_t::static_size;
+        if (!encoded_byte_len || !c->check_assign (out->length, encoded_byte_len, HB_SERIALIZE_ERROR_INT_OVERFLOW)) {
+          c->revert (snap);
+          hb_free (name_str_utf16_be);
+          return_trace (nullptr);
+        }
+  
+        encoded_bytes = hb_bytes_t (name_str_utf16_be, encoded_byte_len);
+      }
+      else
+      {
+        // mac platform, copy the UTF-8 string(all ascii characters) as is
+        if (!c->check_assign (out->length, encoded_bytes.length, HB_SERIALIZE_ERROR_INT_OVERFLOW)) {
+          c->revert (snap);
+          return_trace (nullptr);
+        }
+      }
+
+      out->offset = 0;
+      c->push ();
+      encoded_bytes.copy (c);
+      c->add_link (out->offset, c->pop_pack (), hb_serialize_context_t::Tail, 0);
+      hb_free (name_str_utf16_be);
+    }
+    else
+#endif
+    {
+      out->offset.serialize_copy (c, offset, base, 0, hb_serialize_context_t::Tail, length);
+    }
+    return_trace (out);
+  }
+
+  bool isUnicode () const
+  {
+    unsigned int p = platformID;
+    unsigned int e = encodingID;
+
+    return (p == 0 ||
+	    (p == 3 && (e == 0 || e == 1 || e == 10)));
+  }
+
+  static int cmp (const void *pa, const void *pb)
+  {
+    const NameRecord *a = (const NameRecord *)pa;
+    const NameRecord *b = (const NameRecord *)pb;
+
+    if (a->platformID != b->platformID)
+      return a->platformID - b->platformID;
+
+    if (a->encodingID != b->encodingID)
+      return a->encodingID - b->encodingID;
+
+    if (a->languageID != b->languageID)
+      return a->languageID - b->languageID;
+
+    if (a->nameID != b->nameID)
+      return a->nameID - b->nameID;
+
+    if (a->length != b->length)
+      return a->length - b->length;
+
+    return 0;
+  }
+
+  bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) && offset.sanitize (c, base, length));
+  }
+
+  HBUINT16	platformID;	/* Platform ID. */
+  HBUINT16	encodingID;	/* Platform-specific encoding ID. */
+  HBUINT16	languageID;	/* Language ID. */
+  HBUINT16	nameID;		/* Name ID. */
+  HBUINT16	length;		/* String length (in bytes). */
+  NNOffset16To<UnsizedArrayOf<HBUINT8>>
+		offset;		/* String offset from start of storage area (in bytes). */
+  public:
+  DEFINE_SIZE_STATIC (12);
+};
+
+static int
+_hb_ot_name_entry_cmp_key (const void *pa, const void *pb, bool exact)
+{
+  const hb_ot_name_entry_t *a = (const hb_ot_name_entry_t *) pa;
+  const hb_ot_name_entry_t *b = (const hb_ot_name_entry_t *) pb;
+
+  /* Compare by name_id, then language. */
+
+  if (a->name_id != b->name_id)
+    return a->name_id - b->name_id;
+
+  if (a->language == b->language) return 0;
+  if (!a->language) return -1;
+  if (!b->language) return +1;
+
+  const char *astr = hb_language_to_string (a->language);
+  const char *bstr = hb_language_to_string (b->language);
+
+  signed c = strcmp (astr, bstr);
+
+  // 'a' is the user request, and 'b' is string in the font.
+  // If eg. user asks for "en-us" and font has "en", approve.
+  if (!exact && c &&
+      hb_language_matches (b->language, a->language))
+    return 0;
+
+  return c;
+}
+
+static int
+_hb_ot_name_entry_cmp (const void *pa, const void *pb)
+{
+  /* Compare by name_id, then language, then score, then index. */
+
+  int v = _hb_ot_name_entry_cmp_key (pa, pb, true);
+  if (v)
+    return v;
+
+  const hb_ot_name_entry_t *a = (const hb_ot_name_entry_t *) pa;
+  const hb_ot_name_entry_t *b = (const hb_ot_name_entry_t *) pb;
+
+  if (a->entry_score != b->entry_score)
+    return a->entry_score - b->entry_score;
+
+  if (a->entry_index != b->entry_index)
+    return a->entry_index - b->entry_index;
+
+  return 0;
+}
+
+struct name
+{
+  static constexpr hb_tag_t tableTag = HB_OT_TAG_name;
+
+  unsigned int get_size () const
+  { return min_size + count * nameRecordZ.item_size; }
+
+  template <typename Iterator,
+	    hb_requires (hb_is_source_of (Iterator, const NameRecord &))>
+  bool serialize (hb_serialize_context_t *c,
+		  Iterator it,
+		  const void *src_string_pool
+#ifdef HB_EXPERIMENTAL_API
+                  , const hb_vector_t<hb_ot_name_record_ids_t>& insert_name_records
+		  , const hb_hashmap_t<hb_ot_name_record_ids_t, hb_bytes_t> *name_table_overrides
+#endif
+		  )
+  {
+    TRACE_SERIALIZE (this);
+
+    if (unlikely (!c->extend_min ((*this))))  return_trace (false);
+
+    unsigned total_count = it.len ()
+#ifdef HB_EXPERIMENTAL_API
+        + insert_name_records.length
+#endif
+        ;
+    this->format = 0;
+    if (!c->check_assign (this->count, total_count, HB_SERIALIZE_ERROR_INT_OVERFLOW))
+      return false;
+
+    NameRecord *name_records = (NameRecord *) hb_calloc (total_count, NameRecord::static_size);
+    if (unlikely (!name_records)) return_trace (false);
+
+    hb_array_t<NameRecord> records (name_records, total_count);
+
+    for (const NameRecord& record : it)
+    {
+      hb_memcpy (name_records, &record, NameRecord::static_size);
+      name_records++;
+    }
+
+#ifdef HB_EXPERIMENTAL_API
+    for (unsigned i = 0; i < insert_name_records.length; i++)
+    {
+      const hb_ot_name_record_ids_t& ids = insert_name_records[i];
+      NameRecord record;
+      record.platformID = ids.platform_id;
+      record.encodingID = ids.encoding_id;
+      record.languageID = ids.language_id;
+      record.nameID = ids.name_id;
+      record.length = 0; // handled in NameRecord copy()
+      record.offset = 0;
+      memcpy (name_records, &record, NameRecord::static_size);
+      name_records++;
+    }
+#endif
+
+    records.qsort ();
+
+    c->copy_all (records,
+		 src_string_pool
+#ifdef HB_EXPERIMENTAL_API
+		 , name_table_overrides
+#endif
+		 );
+    hb_free (records.arrayZ);
+
+
+    if (unlikely (c->ran_out_of_room ())) return_trace (false);
+
+    this->stringOffset = c->length ();
+
+    return_trace (true);
+  }
+
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+
+    name *name_prime = c->serializer->start_embed<name> ();
+    if (unlikely (!name_prime)) return_trace (false);
+
+#ifdef HB_EXPERIMENTAL_API
+    const hb_hashmap_t<hb_ot_name_record_ids_t, hb_bytes_t> *name_table_overrides =
+        &c->plan->name_table_overrides;
+#endif
+    
+    auto it =
+    + nameRecordZ.as_array (count)
+    | hb_filter (c->plan->name_ids, &NameRecord::nameID)
+    | hb_filter (c->plan->name_languages, &NameRecord::languageID)
+    | hb_filter ([&] (const NameRecord& namerecord) {
+      return
+          (c->plan->flags & HB_SUBSET_FLAGS_NAME_LEGACY)
+          || namerecord.isUnicode ();
+    })
+#ifdef HB_EXPERIMENTAL_API
+    | hb_filter ([&] (const NameRecord& namerecord) {
+      if (name_table_overrides->is_empty ())
+        return true;
+      hb_ot_name_record_ids_t rec_ids (namerecord.platformID,
+                                       namerecord.encodingID,
+                                       namerecord.languageID,
+                                       namerecord.nameID);
+
+      hb_bytes_t *p;
+      if (name_table_overrides->has (rec_ids, &p) &&
+          (*p).length == 0)
+        return false;
+      return true;
+    })
+#endif
+    ;
+
+#ifdef HB_EXPERIMENTAL_API
+    hb_hashmap_t<hb_ot_name_record_ids_t, unsigned> retained_name_record_ids;
+    for (const NameRecord& rec : it)
+    {
+      hb_ot_name_record_ids_t rec_ids (rec.platformID,
+                                       rec.encodingID,
+                                       rec.languageID,
+                                       rec.nameID);
+      retained_name_record_ids.set (rec_ids, 1);
+    }
+
+    hb_vector_t<hb_ot_name_record_ids_t> insert_name_records;
+    if (!name_table_overrides->is_empty ())
+    {
+      if (unlikely (!insert_name_records.alloc (name_table_overrides->get_population (), true)))
+        return_trace (false);
+      for (const auto& record_ids : name_table_overrides->keys ())
+      {
+        if (name_table_overrides->get (record_ids).length == 0)
+          continue;
+        if (retained_name_record_ids.has (record_ids))
+          continue;
+        insert_name_records.push (record_ids);
+      }
+    }
+#endif
+
+    return (name_prime->serialize (c->serializer, it,
+                                   std::addressof (this + stringOffset)
+#ifdef HB_EXPERIMENTAL_API
+                                   , insert_name_records
+                                   , name_table_overrides
+#endif
+                                   ));
+  }
+
+  bool sanitize_records (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    const void *string_pool = (this+stringOffset).arrayZ;
+    return_trace (nameRecordZ.sanitize (c, count, string_pool));
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  likely (format == 0 || format == 1) &&
+		  c->check_array (nameRecordZ.arrayZ, count) &&
+		  c->check_range (this, stringOffset) &&
+		  sanitize_records (c));
+  }
+
+  struct accelerator_t
+  {
+    accelerator_t (hb_face_t *face)
+    {
+      this->table = hb_sanitize_context_t ().reference_table<name> (face);
+      assert (this->table.get_length () >= this->table->stringOffset);
+      this->pool = (const char *) (const void *) (this->table+this->table->stringOffset);
+      this->pool_len = this->table.get_length () - this->table->stringOffset;
+      const hb_array_t<const NameRecord> all_names (this->table->nameRecordZ.arrayZ,
+						    this->table->count);
+
+      this->names.alloc (all_names.length, true);
+
+      for (unsigned int i = 0; i < all_names.length; i++)
+      {
+	hb_ot_name_entry_t *entry = this->names.push ();
+
+	entry->name_id = all_names[i].nameID;
+	entry->language = all_names[i].language (face);
+	entry->entry_score =  all_names[i].score ();
+	entry->entry_index = i;
+      }
+
+      this->names.qsort (_hb_ot_name_entry_cmp);
+      /* Walk and pick best only for each name_id,language pair,
+       * while dropping unsupported encodings. */
+      unsigned int j = 0;
+      for (unsigned int i = 0; i < this->names.length; i++)
+      {
+	if (this->names[i].entry_score == UNSUPPORTED ||
+	    this->names[i].language == HB_LANGUAGE_INVALID)
+	  continue;
+	if (i &&
+	    this->names[i - 1].name_id  == this->names[i].name_id &&
+	    this->names[i - 1].language == this->names[i].language)
+	  continue;
+	this->names[j++] = this->names[i];
+      }
+      this->names.resize (j);
+    }
+    ~accelerator_t ()
+    {
+      this->table.destroy ();
+    }
+
+    int get_index (hb_ot_name_id_t  name_id,
+		   hb_language_t    language,
+		   unsigned int    *width=nullptr) const
+    {
+      const hb_ot_name_entry_t key = {name_id, {0}, language};
+      const hb_ot_name_entry_t *entry = hb_bsearch (key, (const hb_ot_name_entry_t *) this->names,
+						    this->names.length,
+						    sizeof (hb_ot_name_entry_t),
+						    _hb_ot_name_entry_cmp_key,
+						    true);
+
+      if (!entry)
+      {
+	entry = hb_bsearch (key, (const hb_ot_name_entry_t *) this->names,
+			    this->names.length,
+			    sizeof (hb_ot_name_entry_t),
+			    _hb_ot_name_entry_cmp_key,
+			    false);
+      }
+
+      if (!entry)
+	return -1;
+
+      if (width)
+	*width = entry->entry_score < 10 ? 2 : 1;
+
+      return entry->entry_index;
+    }
+
+    hb_bytes_t get_name (unsigned int idx) const
+    {
+      const hb_array_t<const NameRecord> all_names (table->nameRecordZ.arrayZ, table->count);
+      const NameRecord &record = all_names[idx];
+      const hb_bytes_t string_pool (pool, pool_len);
+      return string_pool.sub_array (record.offset, record.length);
+    }
+
+    private:
+    const char *pool;
+    unsigned int pool_len;
+    public:
+    hb_blob_ptr_t<name> table;
+    hb_vector_t<hb_ot_name_entry_t> names;
+  };
+
+  public:
+  /* We only implement format 0 for now. */
+  HBUINT16	format;		/* Format selector (=0/1). */
+  HBUINT16	count;		/* Number of name records. */
+  NNOffset16To<UnsizedArrayOf<HBUINT8>>
+		stringOffset;	/* Offset to start of string storage (from start of table). */
+  UnsizedArrayOf<NameRecord>
+		nameRecordZ;	/* The name records where count is the number of records. */
+  public:
+  DEFINE_SIZE_ARRAY (6, nameRecordZ);
+};
+
+#undef entry_index
+#undef entry_score
+
+struct name_accelerator_t : name::accelerator_t {
+  name_accelerator_t (hb_face_t *face) : name::accelerator_t (face) {}
+};
+
+} /* namespace OT */
+
+
+#endif /* OT_NAME_NAME_HH */

+ 12 - 5
thirdparty/harfbuzz/src/graph/graph.hh

@@ -123,7 +123,7 @@ struct graph_t
         while (a || b)
         {
           DEBUG_MSG (SUBSET_REPACK, nullptr,
-                     "  0x%x %s 0x%x", *a, (*a == *b) ? "==" : "!=", *b);
+                     "  0x%x %s 0x%x", (unsigned) *a, (*a == *b) ? "==" : "!=", (unsigned) *b);
           a++;
           b++;
         }
@@ -700,6 +700,9 @@ struct graph_t
       }
     }
 
+    if (in_error ())
+      return false;
+
     if (!made_changes)
       return false;
 
@@ -833,7 +836,11 @@ struct graph_t
     if (index_map.has (node_idx))
       return;
 
-    index_map.set (node_idx, duplicate (node_idx));
+    unsigned clone_idx = duplicate (node_idx);
+    if (!check_success (clone_idx != (unsigned) -1))
+      return;
+
+    index_map.set (node_idx, clone_idx);
     for (const auto& l : object (node_idx).all_links ()) {
       duplicate_subgraph (l.objidx, index_map);
     }
@@ -918,12 +925,12 @@ struct graph_t
     {
       // Can't duplicate this node, doing so would orphan the original one as all remaining links
       // to child are from parent.
-      DEBUG_MSG (SUBSET_REPACK, nullptr, "  Not duplicating %d => %d",
+      DEBUG_MSG (SUBSET_REPACK, nullptr, "  Not duplicating %u => %u",
                  parent_idx, child_idx);
       return -1;
     }
 
-    DEBUG_MSG (SUBSET_REPACK, nullptr, "  Duplicating %d => %d",
+    DEBUG_MSG (SUBSET_REPACK, nullptr, "  Duplicating %u => %u",
                parent_idx, child_idx);
 
     unsigned clone_idx = duplicate (child_idx);
@@ -981,7 +988,7 @@ struct graph_t
    */
   bool raise_childrens_priority (unsigned parent_idx)
   {
-    DEBUG_MSG (SUBSET_REPACK, nullptr, "  Raising priority of all children of %d",
+    DEBUG_MSG (SUBSET_REPACK, nullptr, "  Raising priority of all children of %u",
                parent_idx);
     // This operation doesn't change ordering until a sort is run, so no need
     // to invalidate positions. It does not change graph structure so no need

+ 3 - 3
thirdparty/harfbuzz/src/graph/serialize.hh

@@ -153,8 +153,8 @@ void print_overflows (graph_t& graph,
     const auto& child = graph.vertices_[o.child];
     DEBUG_MSG (SUBSET_REPACK, nullptr,
                "  overflow from "
-               "%4d (%4d in, %4d out, space %2d) => "
-               "%4d (%4d in, %4d out, space %2d)",
+               "%4u (%4u in, %4u out, space %2u) => "
+               "%4u (%4u in, %4u out, space %2u)",
                o.parent,
                parent.incoming_edges (),
                parent.obj.real_links.length + parent.obj.virtual_links.length,
@@ -165,7 +165,7 @@ void print_overflows (graph_t& graph,
                graph.space_for (o.child));
   }
   if (overflows.length > 10) {
-    DEBUG_MSG (SUBSET_REPACK, nullptr, "  ... plus %d more overflows.", overflows.length - 10);
+    DEBUG_MSG (SUBSET_REPACK, nullptr, "  ... plus %u more overflows.", overflows.length - 10);
   }
 }
 

+ 0 - 119
thirdparty/harfbuzz/src/graph/test-classdef-graph.cc

@@ -1,119 +0,0 @@
-/*
- * Copyright © 2022  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
- */
-
-#include "gsubgpos-context.hh"
-#include "classdef-graph.hh"
-
-typedef hb_pair_t<hb_codepoint_t, hb_codepoint_t> gid_and_class_t;
-typedef hb_vector_t<gid_and_class_t> gid_and_class_list_t;
-
-
-static bool incremental_size_is (const gid_and_class_list_t& list, unsigned klass,
-                                 unsigned cov_expected, unsigned class_def_expected)
-{
-  graph::class_def_size_estimator_t estimator (list.iter ());
-
-  unsigned result = estimator.incremental_coverage_size (klass);
-  if (result != cov_expected)
-  {
-    printf ("FAIL: coverage expected size %u but was %u\n", cov_expected, result);
-    return false;
-  }
-
-  result = estimator.incremental_class_def_size (klass);
-  if (result != class_def_expected)
-  {
-    printf ("FAIL: class def expected size %u but was %u\n", class_def_expected, result);
-    return false;
-  }
-
-  return true;
-}
-
-static void test_class_and_coverage_size_estimates ()
-{
-  gid_and_class_list_t empty = {
-  };
-  assert (incremental_size_is (empty, 0, 0, 0));
-  assert (incremental_size_is (empty, 1, 0, 0));
-
-  gid_and_class_list_t class_zero = {
-    {5, 0},
-  };
-  assert (incremental_size_is (class_zero, 0, 2, 0));
-
-  gid_and_class_list_t consecutive = {
-    {4, 0},
-    {5, 0},
-    {6, 1},
-    {7, 1},
-    {8, 2},
-    {9, 2},
-    {10, 2},
-    {11, 2},
-  };
-  assert (incremental_size_is (consecutive, 0, 4, 0));
-  assert (incremental_size_is (consecutive, 1, 4, 4));
-  assert (incremental_size_is (consecutive, 2, 8, 6));
-
-  gid_and_class_list_t non_consecutive = {
-    {4, 0},
-    {5, 0},
-
-    {6, 1},
-    {7, 1},
-
-    {9, 2},
-    {10, 2},
-    {11, 2},
-    {12, 2},
-  };
-  assert (incremental_size_is (non_consecutive, 0, 4, 0));
-  assert (incremental_size_is (non_consecutive, 1, 4, 6));
-  assert (incremental_size_is (non_consecutive, 2, 8, 6));
-
-  gid_and_class_list_t multiple_ranges = {
-    {4, 0},
-    {5, 0},
-
-    {6, 1},
-    {7, 1},
-
-    {9, 1},
-
-    {11, 1},
-    {12, 1},
-    {13, 1},
-  };
-  assert (incremental_size_is (multiple_ranges, 0, 4, 0));
-  assert (incremental_size_is (multiple_ranges, 1, 2 * 6, 3 * 6));
-}
-
-int
-main (int argc, char **argv)
-{
-  test_class_and_coverage_size_estimates ();
-}

+ 68 - 37
thirdparty/harfbuzz/src/hb-aat-layout-common.hh

@@ -28,6 +28,7 @@
 #define HB_AAT_LAYOUT_COMMON_HH
 
 #include "hb-aat-layout.hh"
+#include "hb-aat-map.hh"
 #include "hb-open-type.hh"
 
 namespace OT {
@@ -39,6 +40,43 @@ namespace AAT {
 using namespace OT;
 
 
+struct ankr;
+
+struct hb_aat_apply_context_t :
+       hb_dispatch_context_t<hb_aat_apply_context_t, bool, HB_DEBUG_APPLY>
+{
+  const char *get_name () { return "APPLY"; }
+  template <typename T>
+  return_t dispatch (const T &obj) { return obj.apply (this); }
+  static return_t default_return_value () { return false; }
+  bool stop_sublookup_iteration (return_t r) const { return r; }
+
+  const hb_ot_shape_plan_t *plan;
+  hb_font_t *font;
+  hb_face_t *face;
+  hb_buffer_t *buffer;
+  hb_sanitize_context_t sanitizer;
+  const ankr *ankr_table;
+  const OT::GDEF *gdef_table;
+  const hb_sorted_vector_t<hb_aat_map_t::range_flags_t> *range_flags = nullptr;
+  hb_mask_t subtable_flags = 0;
+
+  /* Unused. For debug tracing only. */
+  unsigned int lookup_index;
+
+  HB_INTERNAL hb_aat_apply_context_t (const hb_ot_shape_plan_t *plan_,
+				      hb_font_t *font_,
+				      hb_buffer_t *buffer_,
+				      hb_blob_t *blob = const_cast<hb_blob_t *> (&Null (hb_blob_t)));
+
+  HB_INTERNAL ~hb_aat_apply_context_t ();
+
+  HB_INTERNAL void set_ankr_table (const AAT::ankr *ankr_table_);
+
+  void set_lookup_index (unsigned int i) { lookup_index = i; }
+};
+
+
 /*
  * Lookup Table
  */
@@ -740,16 +778,44 @@ struct StateTableDriver
 	      num_glyphs (face_->get_num_glyphs ()) {}
 
   template <typename context_t>
-  void drive (context_t *c)
+  void drive (context_t *c, hb_aat_apply_context_t *ac)
   {
     if (!c->in_place)
       buffer->clear_output ();
 
     int state = StateTableT::STATE_START_OF_TEXT;
+    // If there's only one range, we already checked the flag.
+    auto *last_range = ac->range_flags && (ac->range_flags->length > 1) ? &(*ac->range_flags)[0] : nullptr;
     for (buffer->idx = 0; buffer->successful;)
     {
+      /* This block is copied in NoncontextualSubtable::apply. Keep in sync. */
+      if (last_range)
+      {
+	auto *range = last_range;
+	if (buffer->idx < buffer->len)
+	{
+	  unsigned cluster = buffer->cur().cluster;
+	  while (cluster < range->cluster_first)
+	    range--;
+	  while (cluster > range->cluster_last)
+	    range++;
+
+
+	  last_range = range;
+	}
+	if (!(range->flags & ac->subtable_flags))
+	{
+	  if (buffer->idx == buffer->len || unlikely (!buffer->successful))
+	    break;
+
+	  state = StateTableT::STATE_START_OF_TEXT;
+	  (void) buffer->next_glyph ();
+	  continue;
+	}
+      }
+
       unsigned int klass = buffer->idx < buffer->len ?
-			   machine.get_class (buffer->info[buffer->idx].codepoint, num_glyphs) :
+			   machine.get_class (buffer->cur().codepoint, num_glyphs) :
 			   (unsigned) StateTableT::CLASS_END_OF_TEXT;
       DEBUG_MSG (APPLY, nullptr, "c%u at %u", klass, buffer->idx);
       const EntryT &entry = machine.get_entry (state, klass);
@@ -845,41 +911,6 @@ struct StateTableDriver
 };
 
 
-struct ankr;
-
-struct hb_aat_apply_context_t :
-       hb_dispatch_context_t<hb_aat_apply_context_t, bool, HB_DEBUG_APPLY>
-{
-  const char *get_name () { return "APPLY"; }
-  template <typename T>
-  return_t dispatch (const T &obj) { return obj.apply (this); }
-  static return_t default_return_value () { return false; }
-  bool stop_sublookup_iteration (return_t r) const { return r; }
-
-  const hb_ot_shape_plan_t *plan;
-  hb_font_t *font;
-  hb_face_t *face;
-  hb_buffer_t *buffer;
-  hb_sanitize_context_t sanitizer;
-  const ankr *ankr_table;
-  const OT::GDEF *gdef_table;
-
-  /* Unused. For debug tracing only. */
-  unsigned int lookup_index;
-
-  HB_INTERNAL hb_aat_apply_context_t (const hb_ot_shape_plan_t *plan_,
-				      hb_font_t *font_,
-				      hb_buffer_t *buffer_,
-				      hb_blob_t *blob = const_cast<hb_blob_t *> (&Null (hb_blob_t)));
-
-  HB_INTERNAL ~hb_aat_apply_context_t ();
-
-  HB_INTERNAL void set_ankr_table (const AAT::ankr *ankr_table_);
-
-  void set_lookup_index (unsigned int i) { lookup_index = i; }
-};
-
-
 } /* namespace AAT */
 
 

+ 6 - 4
thirdparty/harfbuzz/src/hb-aat-layout-kerx-table.hh

@@ -350,7 +350,7 @@ struct KerxSubTableFormat1
     driver_context_t dc (this, c);
 
     StateTableDriver<Types, EntryData> driver (machine, c->buffer, c->font->face);
-    driver.drive (&dc);
+    driver.drive (&dc, c);
 
     return_trace (true);
   }
@@ -594,7 +594,7 @@ struct KerxSubTableFormat4
     driver_context_t dc (this, c);
 
     StateTableDriver<Types, EntryData> driver (machine, c->buffer, c->font->face);
-    driver.drive (&dc);
+    driver.drive (&dc, c);
 
     return_trace (true);
   }
@@ -869,6 +869,8 @@ struct KerxTable
 
   bool apply (AAT::hb_aat_apply_context_t *c) const
   {
+    c->buffer->unsafe_to_concat ();
+
     typedef typename T::SubTable SubTable;
 
     bool ret = false;
@@ -889,7 +891,7 @@ struct KerxTable
       reverse = bool (st->u.header.coverage & st->u.header.Backwards) !=
 		HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction);
 
-      if (!c->buffer->message (c->font, "start subtable %d", c->lookup_index))
+      if (!c->buffer->message (c->font, "start subtable %u", c->lookup_index))
 	goto skip;
 
       if (!seenCrossStream &&
@@ -921,7 +923,7 @@ struct KerxTable
       if (reverse)
 	c->buffer->reverse ();
 
-      (void) c->buffer->message (c->font, "end subtable %d", c->lookup_index);
+      (void) c->buffer->message (c->font, "end subtable %u", c->lookup_index);
 
     skip:
       st = &StructAfter<SubTable> (*st);

+ 43 - 14
thirdparty/harfbuzz/src/hb-aat-layout-morx-table.hh

@@ -169,7 +169,7 @@ struct RearrangementSubtable
     driver_context_t dc (this);
 
     StateTableDriver<Types, EntryData> driver (machine, c->buffer, c->face);
-    driver.drive (&dc);
+    driver.drive (&dc, c);
 
     return_trace (dc.ret);
   }
@@ -325,7 +325,7 @@ struct ContextualSubtable
     driver_context_t dc (this, c);
 
     StateTableDriver<Types, EntryData> driver (machine, c->buffer, c->face);
-    driver.drive (&dc);
+    driver.drive (&dc, c);
 
     return_trace (dc.ret);
   }
@@ -525,7 +525,7 @@ struct LigatureSubtable
 	  if (unlikely (!componentData.sanitize (&c->sanitizer))) break;
 	  ligature_idx += componentData;
 
-	  DEBUG_MSG (APPLY, nullptr, "Action store %u last %u",
+	  DEBUG_MSG (APPLY, nullptr, "Action store %d last %d",
 		     bool (action & LigActionStore),
 		     bool (action & LigActionLast));
 	  if (action & (LigActionStore | LigActionLast))
@@ -577,7 +577,7 @@ struct LigatureSubtable
     driver_context_t dc (this, c);
 
     StateTableDriver<Types, EntryData> driver (machine, c->buffer, c->face);
-    driver.drive (&dc);
+    driver.drive (&dc, c);
 
     return_trace (dc.ret);
   }
@@ -618,8 +618,27 @@ struct NoncontextualSubtable
 
     hb_glyph_info_t *info = c->buffer->info;
     unsigned int count = c->buffer->len;
+    // If there's only one range, we already checked the flag.
+    auto *last_range = c->range_flags && (c->range_flags->length > 1) ? &(*c->range_flags)[0] : nullptr;
     for (unsigned int i = 0; i < count; i++)
     {
+      /* This block copied from StateTableDriver::drive. Keep in sync. */
+      if (last_range)
+      {
+	auto *range = last_range;
+	{
+	  unsigned cluster = info[i].cluster;
+	  while (cluster < range->cluster_first)
+	    range--;
+	  while (cluster > range->cluster_last)
+	    range++;
+
+	  last_range = range;
+	}
+	if (!(range->flags & c->subtable_flags))
+	  continue;
+      }
+
       const HBGlyphID16 *replacement = substitute.get_value (info[i].codepoint, num_glyphs);
       if (replacement)
       {
@@ -820,7 +839,7 @@ struct InsertionSubtable
     driver_context_t dc (this, c);
 
     StateTableDriver<Types, EntryData> driver (machine, c->buffer, c->face);
-    driver.drive (&dc);
+    driver.drive (&dc, c);
 
     return_trace (dc.ret);
   }
@@ -968,7 +987,7 @@ struct Chain
 	// Check whether this type/setting pair was requested in the map, and if so, apply its flags.
 	// (The search here only looks at the type and setting fields of feature_info_t.)
 	hb_aat_map_builder_t::feature_info_t info = { type, setting, false, 0 };
-	if (map->features.bsearch (info))
+	if (map->current_features.bsearch (info))
 	{
 	  flags &= feature.disableFlags;
 	  flags |= feature.enableFlags;
@@ -994,8 +1013,7 @@ struct Chain
     return flags;
   }
 
-  void apply (hb_aat_apply_context_t *c,
-	      hb_mask_t flags) const
+  void apply (hb_aat_apply_context_t *c) const
   {
     const ChainSubtable<Types> *subtable = &StructAfter<ChainSubtable<Types>> (featureZ.as_array (featureCount));
     unsigned int count = subtableCount;
@@ -1003,8 +1021,10 @@ struct Chain
     {
       bool reverse;
 
-      if (!(subtable->subFeatureFlags & flags))
+      if (hb_none (hb_iter (c->range_flags) |
+		   hb_map ([&subtable] (const hb_aat_map_t::range_flags_t _) -> bool { return subtable->subFeatureFlags & (_.flags); })))
 	goto skip;
+      c->subtable_flags = subtable->subFeatureFlags;
 
       if (!(subtable->get_coverage() & ChainSubtable<Types>::AllDirections) &&
 	  HB_DIRECTION_IS_VERTICAL (c->buffer->props.direction) !=
@@ -1043,7 +1063,7 @@ struct Chain
 		bool (subtable->get_coverage () & ChainSubtable<Types>::Backwards) !=
 		HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction);
 
-      if (!c->buffer->message (c->font, "start chainsubtable %d", c->lookup_index))
+      if (!c->buffer->message (c->font, "start chainsubtable %u", c->lookup_index))
 	goto skip;
 
       if (reverse)
@@ -1054,7 +1074,7 @@ struct Chain
       if (reverse)
 	c->buffer->reverse ();
 
-      (void) c->buffer->message (c->font, "end chainsubtable %d", c->lookup_index);
+      (void) c->buffer->message (c->font, "end chainsubtable %u", c->lookup_index);
 
       if (unlikely (!c->buffer->successful)) return;
 
@@ -1120,22 +1140,31 @@ struct mortmorx
   {
     const Chain<Types> *chain = &firstChain;
     unsigned int count = chainCount;
+    if (unlikely (!map->chain_flags.resize (count)))
+      return;
     for (unsigned int i = 0; i < count; i++)
     {
-      map->chain_flags.push (chain->compile_flags (mapper));
+      map->chain_flags[i].push (hb_aat_map_t::range_flags_t {chain->compile_flags (mapper),
+							     mapper->range_first,
+							     mapper->range_last});
       chain = &StructAfter<Chain<Types>> (*chain);
     }
   }
 
-  void apply (hb_aat_apply_context_t *c) const
+  void apply (hb_aat_apply_context_t *c,
+	      const hb_aat_map_t &map) const
   {
     if (unlikely (!c->buffer->successful)) return;
+
+    c->buffer->unsafe_to_concat ();
+
     c->set_lookup_index (0);
     const Chain<Types> *chain = &firstChain;
     unsigned int count = chainCount;
     for (unsigned int i = 0; i < count; i++)
     {
-      chain->apply (c, c->plan->aat_map.chain_flags[i]);
+      c->range_flags = &map.chain_flags[i];
+      chain->apply (c);
       if (unlikely (!c->buffer->successful)) return;
       chain = &StructAfter<Chain<Types>> (*chain);
     }

+ 11 - 3
thirdparty/harfbuzz/src/hb-aat-layout.cc

@@ -244,15 +244,23 @@ hb_aat_layout_has_substitution (hb_face_t *face)
 void
 hb_aat_layout_substitute (const hb_ot_shape_plan_t *plan,
 			  hb_font_t *font,
-			  hb_buffer_t *buffer)
+			  hb_buffer_t *buffer,
+			  const hb_feature_t *features,
+			  unsigned num_features)
 {
+  hb_aat_map_builder_t builder (font->face, plan->props);
+  for (unsigned i = 0; i < num_features; i++)
+    builder.add_feature (features[i]);
+  hb_aat_map_t map;
+  builder.compile (map);
+
   hb_blob_t *morx_blob = font->face->table.morx.get_blob ();
   const AAT::morx& morx = *morx_blob->as<AAT::morx> ();
   if (morx.has_data ())
   {
     AAT::hb_aat_apply_context_t c (plan, font, buffer, morx_blob);
     if (!buffer->message (font, "start table morx")) return;
-    morx.apply (&c);
+    morx.apply (&c, map);
     (void) buffer->message (font, "end table morx");
     return;
   }
@@ -263,7 +271,7 @@ hb_aat_layout_substitute (const hb_ot_shape_plan_t *plan,
   {
     AAT::hb_aat_apply_context_t c (plan, font, buffer, mort_blob);
     if (!buffer->message (font, "start table mort")) return;
-    mort.apply (&c);
+    mort.apply (&c, map);
     (void) buffer->message (font, "end table mort");
     return;
   }

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

@@ -53,7 +53,9 @@ hb_aat_layout_compile_map (const hb_aat_map_builder_t *mapper,
 HB_INTERNAL void
 hb_aat_layout_substitute (const hb_ot_shape_plan_t *plan,
 			  hb_font_t *font,
-			  hb_buffer_t *buffer);
+			  hb_buffer_t *buffer,
+			  const hb_feature_t *features,
+			  unsigned num_features);
 
 HB_INTERNAL void
 hb_aat_layout_zero_width_deleted_glyphs (hb_buffer_t *buffer);

+ 100 - 30
thirdparty/harfbuzz/src/hb-aat-map.cc

@@ -36,27 +36,29 @@
 #include "hb-aat-layout-feat-table.hh"
 
 
-void hb_aat_map_builder_t::add_feature (hb_tag_t tag, unsigned value)
+void hb_aat_map_builder_t::add_feature (const hb_feature_t &feature)
 {
   if (!face->table.feat->has_data ()) return;
 
-  if (tag == HB_TAG ('a','a','l','t'))
+  if (feature.tag == HB_TAG ('a','a','l','t'))
   {
     if (!face->table.feat->exposes_feature (HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES))
       return;
-    feature_info_t *info = features.push();
-    info->type = HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES;
-    info->setting = (hb_aat_layout_feature_selector_t) value;
-    info->seq = features.length;
-    info->is_exclusive = true;
+    feature_range_t *range = features.push();
+    range->start = feature.start;
+    range->end = feature.end;
+    range->info.type = HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES;
+    range->info.setting = (hb_aat_layout_feature_selector_t) feature.value;
+    range->info.seq = features.length;
+    range->info.is_exclusive = true;
     return;
   }
 
-  const hb_aat_feature_mapping_t *mapping = hb_aat_layout_find_feature_mapping (tag);
+  const hb_aat_feature_mapping_t *mapping = hb_aat_layout_find_feature_mapping (feature.tag);
   if (!mapping) return;
 
-  const AAT::FeatureName* feature = &face->table.feat->get_feature (mapping->aatFeatureType);
-  if (!feature->has_data ())
+  const AAT::FeatureName* feature_name = &face->table.feat->get_feature (mapping->aatFeatureType);
+  if (!feature_name->has_data ())
   {
     /* Special case: Chain::compile_flags will fall back to the deprecated version of
      * small-caps if necessary, so we need to check for that possibility.
@@ -64,38 +66,106 @@ void hb_aat_map_builder_t::add_feature (hb_tag_t tag, unsigned value)
     if (mapping->aatFeatureType == HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE &&
 	mapping->selectorToEnable == HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS)
     {
-      feature = &face->table.feat->get_feature (HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE);
-      if (!feature->has_data ()) return;
+      feature_name = &face->table.feat->get_feature (HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE);
+      if (!feature_name->has_data ()) return;
     }
     else return;
   }
 
-  feature_info_t *info = features.push();
-  info->type = mapping->aatFeatureType;
-  info->setting = value ? mapping->selectorToEnable : mapping->selectorToDisable;
-  info->seq = features.length;
-  info->is_exclusive = feature->is_exclusive ();
+  feature_range_t *range = features.push();
+  range->start = feature.start;
+  range->end = feature.end;
+  range->info.type = mapping->aatFeatureType;
+  range->info.setting = feature.value ? mapping->selectorToEnable : mapping->selectorToDisable;
+  range->info.seq = features.length;
+  range->info.is_exclusive = feature_name->is_exclusive ();
 }
 
 void
 hb_aat_map_builder_t::compile (hb_aat_map_t  &m)
 {
-  /* Sort features and merge duplicates */
-  if (features.length)
+  /* Compute active features per range, and compile each. */
+
+  /* Sort features by start/end events. */
+  hb_vector_t<feature_event_t> feature_events;
+  for (unsigned int i = 0; i < features.length; i++)
+  {
+    auto &feature = features[i];
+
+    if (features[i].start == features[i].end)
+      continue;
+
+    feature_event_t *event;
+
+    event = feature_events.push ();
+    event->index = features[i].start;
+    event->start = true;
+    event->feature = feature.info;
+
+    event = feature_events.push ();
+    event->index = features[i].end;
+    event->start = false;
+    event->feature = feature.info;
+  }
+  feature_events.qsort ();
+  /* Add a strategic final event. */
+  {
+    feature_info_t feature;
+    feature.seq = features.length + 1;
+
+    feature_event_t *event = feature_events.push ();
+    event->index = -1; /* This value does magic. */
+    event->start = false;
+    event->feature = feature;
+  }
+
+  /* Scan events and save features for each range. */
+  hb_sorted_vector_t<feature_info_t> active_features;
+  unsigned int last_index = 0;
+  for (unsigned int i = 0; i < feature_events.length; i++)
   {
-    features.qsort ();
-    unsigned int j = 0;
-    for (unsigned int i = 1; i < features.length; i++)
-      if (features[i].type != features[j].type ||
-	  /* Nonexclusive feature selectors come in even/odd pairs to turn a setting on/off
-	   * respectively, so we mask out the low-order bit when checking for "duplicates"
-	   * (selectors referring to the same feature setting) here. */
-	  (!features[i].is_exclusive && ((features[i].setting & ~1) != (features[j].setting & ~1))))
-	features[++j] = features[i];
-    features.shrink (j + 1);
+    feature_event_t *event = &feature_events[i];
+
+    if (event->index != last_index)
+    {
+      /* Save a snapshot of active features and the range. */
+
+      /* Sort features and merge duplicates */
+      current_features = active_features;
+      range_first = last_index;
+      range_last = event->index - 1;
+      if (current_features.length)
+      {
+	current_features.qsort ();
+	unsigned int j = 0;
+	for (unsigned int i = 1; i < current_features.length; i++)
+	  if (current_features[i].type != current_features[j].type ||
+	      /* Nonexclusive feature selectors come in even/odd pairs to turn a setting on/off
+	       * respectively, so we mask out the low-order bit when checking for "duplicates"
+	       * (selectors referring to the same feature setting) here. */
+	      (!current_features[i].is_exclusive && ((current_features[i].setting & ~1) != (current_features[j].setting & ~1))))
+	    current_features[++j] = current_features[i];
+	current_features.shrink (j + 1);
+      }
+
+      hb_aat_layout_compile_map (this, &m);
+
+      last_index = event->index;
+    }
+
+    if (event->start)
+    {
+      active_features.push (event->feature);
+    } else {
+      feature_info_t *feature = active_features.lsearch (event->feature);
+      if (feature)
+	active_features.remove_ordered (feature - active_features.arrayZ);
+    }
   }
 
-  hb_aat_layout_compile_map (this, &m);
+  for (auto &chain_flags : m.chain_flags)
+    // With our above setup this value is one less than desired; adjust it.
+    chain_flags.tail().cluster_last = HB_FEATURE_GLOBAL_END;
 }
 
 

+ 35 - 10
thirdparty/harfbuzz/src/hb-aat-map.hh

@@ -35,16 +35,15 @@ struct hb_aat_map_t
   friend struct hb_aat_map_builder_t;
 
   public:
-
-  void init ()
+  struct range_flags_t
   {
-    hb_memset (this, 0, sizeof (*this));
-    chain_flags.init ();
-  }
-  void fini () { chain_flags.fini (); }
+    hb_mask_t flags;
+    unsigned cluster_first;
+    unsigned cluster_last; // end - 1
+  };
 
   public:
-  hb_vector_t<hb_mask_t> chain_flags;
+  hb_vector_t<hb_sorted_vector_t<range_flags_t>> chain_flags;
 };
 
 struct hb_aat_map_builder_t
@@ -56,7 +55,7 @@ struct hb_aat_map_builder_t
 				      face (face_),
 				      props (props_) {}
 
-  HB_INTERNAL void add_feature (hb_tag_t tag, unsigned int value=1);
+  HB_INTERNAL void add_feature (const hb_feature_t &feature);
 
   HB_INTERNAL void compile (hb_aat_map_t  &m);
 
@@ -78,7 +77,7 @@ struct hb_aat_map_builder_t
 	    return (a->seq < b->seq ? -1 : a->seq > b->seq ? 1 : 0);
     }
 
-    /* compares type & setting only, not is_exclusive flag or seq number */
+    /* compares type & setting only */
     int cmp (const feature_info_t& f) const
     {
       return (f.type != type) ? (f.type < type ? -1 : 1) :
@@ -86,12 +85,38 @@ struct hb_aat_map_builder_t
     }
   };
 
+  struct feature_range_t
+  {
+    feature_info_t info;
+    unsigned start;
+    unsigned end;
+  };
+
+  private:
+  struct feature_event_t
+  {
+    unsigned int index;
+    bool start;
+    feature_info_t feature;
+
+    HB_INTERNAL static int cmp (const void *pa, const void *pb) {
+      const feature_event_t *a = (const feature_event_t *) pa;
+      const feature_event_t *b = (const feature_event_t *) pb;
+      return a->index < b->index ? -1 : a->index > b->index ? 1 :
+	     a->start < b->start ? -1 : a->start > b->start ? 1 :
+	     feature_info_t::cmp (&a->feature, &b->feature);
+    }
+  };
+
   public:
   hb_face_t *face;
   hb_segment_properties_t props;
 
   public:
-  hb_sorted_vector_t<feature_info_t> features;
+  hb_sorted_vector_t<feature_range_t> features;
+  hb_sorted_vector_t<feature_info_t> current_features;
+  unsigned range_first = HB_FEATURE_GLOBAL_START;
+  unsigned range_last = HB_FEATURE_GLOBAL_END;
 };
 
 

+ 80 - 8
thirdparty/harfbuzz/src/hb-algs.hh

@@ -110,9 +110,10 @@ struct BEInt<Type, 2>
   constexpr operator Type () const
   {
 #if defined(__OPTIMIZE__) && !defined(HB_NO_PACKED) && \
-    ((defined(__GNUC__) && __GNUC__ >= 5) || defined(__clang__)) && \
     defined(__BYTE_ORDER) && \
-    (__BYTE_ORDER == __LITTLE_ENDIAN || __BYTE_ORDER == __BIG_ENDIAN)
+    (__BYTE_ORDER == __BIG_ENDIAN || \
+     (__BYTE_ORDER == __LITTLE_ENDIAN && \
+      hb_has_builtin(__builtin_bswap16)))
     /* Spoon-feed the compiler a big-endian integer with alignment 1.
      * https://github.com/harfbuzz/harfbuzz/pull/1398 */
 #if __BYTE_ORDER == __LITTLE_ENDIAN
@@ -155,9 +156,10 @@ struct BEInt<Type, 4>
   struct __attribute__((packed)) packed_uint32_t { uint32_t v; };
   constexpr operator Type () const {
 #if defined(__OPTIMIZE__) && !defined(HB_NO_PACKED) && \
-    ((defined(__GNUC__) && __GNUC__ >= 5) || defined(__clang__)) && \
     defined(__BYTE_ORDER) && \
-    (__BYTE_ORDER == __LITTLE_ENDIAN || __BYTE_ORDER == __BIG_ENDIAN)
+    (__BYTE_ORDER == __BIG_ENDIAN || \
+     (__BYTE_ORDER == __LITTLE_ENDIAN && \
+      hb_has_builtin(__builtin_bswap32)))
     /* Spoon-feed the compiler a big-endian integer with alignment 1.
      * https://github.com/harfbuzz/harfbuzz/pull/1398 */
 #if __BYTE_ORDER == __LITTLE_ENDIAN
@@ -598,13 +600,17 @@ template <typename T>
 static inline unsigned int
 hb_popcount (T v)
 {
-#if (defined(__GNUC__) && (__GNUC__ >= 4)) || defined(__clang__)
+#if hb_has_builtin(__builtin_popcount)
   if (sizeof (T) <= sizeof (unsigned int))
     return __builtin_popcount (v);
+#endif
 
+#if hb_has_builtin(__builtin_popcountl)
   if (sizeof (T) <= sizeof (unsigned long))
     return __builtin_popcountl (v);
+#endif
 
+#if hb_has_builtin(__builtin_popcountll)
   if (sizeof (T) <= sizeof (unsigned long long))
     return __builtin_popcountll (v);
 #endif
@@ -641,13 +647,17 @@ hb_bit_storage (T v)
 {
   if (unlikely (!v)) return 0;
 
-#if (defined(__GNUC__) && (__GNUC__ >= 4)) || defined(__clang__)
+#if hb_has_builtin(__builtin_clz)
   if (sizeof (T) <= sizeof (unsigned int))
     return sizeof (unsigned int) * 8 - __builtin_clz (v);
+#endif
 
+#if hb_has_builtin(__builtin_clzl)
   if (sizeof (T) <= sizeof (unsigned long))
     return sizeof (unsigned long) * 8 - __builtin_clzl (v);
+#endif
 
+#if hb_has_builtin(__builtin_clzll)
   if (sizeof (T) <= sizeof (unsigned long long))
     return sizeof (unsigned long long) * 8 - __builtin_clzll (v);
 #endif
@@ -715,13 +725,17 @@ hb_ctz (T v)
 {
   if (unlikely (!v)) return 8 * sizeof (T);
 
-#if (defined(__GNUC__) && (__GNUC__ >= 4)) || defined(__clang__)
+#if hb_has_builtin(__builtin_ctz)
   if (sizeof (T) <= sizeof (unsigned int))
     return __builtin_ctz (v);
+#endif
 
+#if hb_has_builtin(__builtin_ctzl)
   if (sizeof (T) <= sizeof (unsigned long))
     return __builtin_ctzl (v);
+#endif
 
+#if hb_has_builtin(__builtin_ctzll)
   if (sizeof (T) <= sizeof (unsigned long long))
     return __builtin_ctzll (v);
 #endif
@@ -875,7 +889,7 @@ hb_in_ranges (T u, T lo1, T hi1, Ts... ds)
 static inline bool
 hb_unsigned_mul_overflows (unsigned int count, unsigned int size, unsigned *result = nullptr)
 {
-#if (defined(__GNUC__) && (__GNUC__ >= 4)) || defined(__clang__)
+#if hb_has_builtin(__builtin_mul_overflow)
   unsigned stack_result;
   if (!result)
     result = &stack_result;
@@ -1330,4 +1344,62 @@ struct
 HB_FUNCOBJ (hb_dec);
 
 
+/* Adapted from kurbo implementation with extra parameters added,
+ * and finding for a particular range instead of 0.
+ *
+ * For documentation and implementation see:
+ *
+ * [ITP method]: https://en.wikipedia.org/wiki/ITP_Method
+ * [An Enhancement of the Bisection Method Average Performance Preserving Minmax Optimality]: https://dl.acm.org/doi/10.1145/3423597
+ * https://docs.rs/kurbo/0.8.1/kurbo/common/fn.solve_itp.html
+ * https://github.com/linebender/kurbo/blob/fd839c25ea0c98576c7ce5789305822675a89938/src/common.rs#L162-L248
+ */
+template <typename func_t>
+double solve_itp (func_t f,
+		  double a, double b,
+		  double epsilon,
+		  double min_y, double max_y,
+		  double &ya, double &yb, double &y)
+{
+  unsigned n1_2 = (unsigned) (hb_max (ceil (log2 ((b - a) / epsilon)) - 1.0, 0.0));
+  const unsigned n0 = 1; // Hardwired
+  const double k1 = 0.2 / (b - a); // Hardwired.
+  unsigned nmax = n0 + n1_2;
+  double scaled_epsilon = epsilon * double (1llu << nmax);
+  double _2_epsilon = 2.0 * epsilon;
+  while (b - a > _2_epsilon)
+  {
+    double x1_2 = 0.5 * (a + b);
+    double r = scaled_epsilon - 0.5 * (b - a);
+    double xf = (yb * a - ya * b) / (yb - ya);
+    double sigma = x1_2 - xf;
+    double b_a = b - a;
+    // This has k2 = 2 hardwired for efficiency.
+    double b_a_k2 = b_a * b_a;
+    double delta = k1 * b_a_k2;
+    int sigma_sign = sigma >= 0 ? +1 : -1;
+    double xt = delta <= fabs (x1_2 - xf) ? xf + delta * sigma_sign : x1_2;
+    double xitp = fabs (xt - x1_2) <= r ? xt : x1_2 - r * sigma_sign;
+    double yitp = f (xitp);
+    if (yitp > max_y)
+    {
+      b = xitp;
+      yb = yitp;
+    }
+    else if (yitp < min_y)
+    {
+      a = xitp;
+      ya = yitp;
+    }
+    else
+    {
+      y = yitp;
+      return xitp;
+    }
+    scaled_epsilon *= 0.5;
+  }
+  return 0.5 * (a + b);
+}
+
+
 #endif /* HB_ALGS_HH */

+ 3 - 0
thirdparty/harfbuzz/src/hb-array.hh

@@ -304,6 +304,9 @@ struct hb_array_t : hb_iter_with_fallback_t<hb_array_t<Type>, Type&>
   unsigned int backwards_length = 0;
 };
 template <typename T> inline hb_array_t<T>
+hb_array ()
+{ return hb_array_t<T> (); }
+template <typename T> inline hb_array_t<T>
 hb_array (T *array, unsigned int length)
 { return hb_array_t<T> (array, length); }
 template <typename T, unsigned int length_> inline hb_array_t<T>

+ 33 - 8
thirdparty/harfbuzz/src/hb-atomic.hh

@@ -84,11 +84,11 @@ _hb_atomic_ptr_impl_cmplexch (const void **P, const void *O_, const void *N)
 #define _hb_memory_r_barrier()			std::atomic_thread_fence(std::memory_order_acquire)
 #define _hb_memory_w_barrier()			std::atomic_thread_fence(std::memory_order_release)
 
-#define hb_atomic_int_impl_add(AI, V)		(reinterpret_cast<std::atomic<int> *> (AI)->fetch_add ((V), std::memory_order_acq_rel))
-#define hb_atomic_int_impl_set_relaxed(AI, V)	(reinterpret_cast<std::atomic<int> *> (AI)->store ((V), std::memory_order_relaxed))
-#define hb_atomic_int_impl_set(AI, V)		(reinterpret_cast<std::atomic<int> *> (AI)->store ((V), std::memory_order_release))
-#define hb_atomic_int_impl_get_relaxed(AI)	(reinterpret_cast<std::atomic<int> const *> (AI)->load (std::memory_order_relaxed))
-#define hb_atomic_int_impl_get(AI)		(reinterpret_cast<std::atomic<int> const *> (AI)->load (std::memory_order_acquire))
+#define hb_atomic_int_impl_add(AI, V)		(reinterpret_cast<std::atomic<std::decay<decltype (*(AI))>::type> *> (AI)->fetch_add ((V), std::memory_order_acq_rel))
+#define hb_atomic_int_impl_set_relaxed(AI, V)	(reinterpret_cast<std::atomic<std::decay<decltype (*(AI))>::type> *> (AI)->store ((V), std::memory_order_relaxed))
+#define hb_atomic_int_impl_set(AI, V)		(reinterpret_cast<std::atomic<std::decay<decltype (*(AI))>::type> *> (AI)->store ((V), std::memory_order_release))
+#define hb_atomic_int_impl_get_relaxed(AI)	(reinterpret_cast<std::atomic<std::decay<decltype (*(AI))>::type> const *> (AI)->load (std::memory_order_relaxed))
+#define hb_atomic_int_impl_get(AI)		(reinterpret_cast<std::atomic<std::decay<decltype (*(AI))>::type> const *> (AI)->load (std::memory_order_acquire))
 
 #define hb_atomic_ptr_impl_set_relaxed(P, V)	(reinterpret_cast<std::atomic<void*> *> (P)->store ((V), std::memory_order_relaxed))
 #define hb_atomic_ptr_impl_get_relaxed(P)	(reinterpret_cast<std::atomic<void*> const *> (P)->load (std::memory_order_relaxed))
@@ -111,10 +111,15 @@ _hb_atomic_ptr_impl_cmplexch (const void **P, const void *O_, const void *N)
 #endif
 
 
+/* This should never be disabled, even under HB_NO_MT.
+ * except that MSVC gives me an internal compiler error, so disabled there.
+ *
+ * https://github.com/harfbuzz/harfbuzz/pull/4119
+ */
 #ifndef _hb_compiler_memory_r_barrier
-/* This we always use std::atomic for; and should never be disabled...
- * except that MSVC gives me an internal compiler error on it. */
-#if !defined(_MSC_VER)
+#if defined(__ATOMIC_ACQUIRE) // gcc-like
+#define _hb_compiler_memory_r_barrier() asm volatile("": : :"memory")
+#elif !defined(_MSC_VER)
 #include <atomic>
 #define _hb_compiler_memory_r_barrier() std::atomic_signal_fence (std::memory_order_acquire)
 #else
@@ -145,15 +150,35 @@ _hb_atomic_ptr_impl_cmplexch (const void **P, const void *O_, const void *N)
 #endif
 #ifndef hb_atomic_int_impl_set
 inline void hb_atomic_int_impl_set (int *AI, int v)	{ _hb_memory_w_barrier (); *AI = v; }
+inline void hb_atomic_int_impl_set (short *AI, short v)	{ _hb_memory_w_barrier (); *AI = v; }
 #endif
 #ifndef hb_atomic_int_impl_get
 inline int hb_atomic_int_impl_get (const int *AI)	{ int v = *AI; _hb_memory_r_barrier (); return v; }
+inline short hb_atomic_int_impl_get (const short *AI)	{ short v = *AI; _hb_memory_r_barrier (); return v; }
 #endif
 #ifndef hb_atomic_ptr_impl_get
 inline void *hb_atomic_ptr_impl_get (void ** const P)	{ void *v = *P; _hb_memory_r_barrier (); return v; }
 #endif
 
 
+struct hb_atomic_short_t
+{
+  hb_atomic_short_t () = default;
+  constexpr hb_atomic_short_t (short v) : v (v) {}
+
+  hb_atomic_short_t& operator = (short v_) { set_relaxed (v_); return *this; }
+  operator short () const { return get_relaxed (); }
+
+  void set_relaxed (short v_) { hb_atomic_int_impl_set_relaxed (&v, v_); }
+  void set_release (short v_) { hb_atomic_int_impl_set (&v, v_); }
+  short get_relaxed () const { return hb_atomic_int_impl_get_relaxed (&v); }
+  short get_acquire () const { return hb_atomic_int_impl_get (&v); }
+  short inc () { return hb_atomic_int_impl_add (&v,  1); }
+  short dec () { return hb_atomic_int_impl_add (&v, -1); }
+
+  short v = 0;
+};
+
 struct hb_atomic_int_t
 {
   hb_atomic_int_t () = default;

+ 18 - 8
thirdparty/harfbuzz/src/hb-bit-page.hh

@@ -34,14 +34,24 @@
 /* Compiler-assisted vectorization. */
 
 /* Type behaving similar to vectorized vars defined using __attribute__((vector_size(...))),
- * basically a fixed-size bitset. */
+ * basically a fixed-size bitset. We can't use the compiler type because hb_vector_t cannot
+ * guarantee alignment requirements. */
 template <typename elt_t, unsigned int byte_size>
 struct hb_vector_size_t
 {
   elt_t& operator [] (unsigned int i) { return v[i]; }
   const elt_t& operator [] (unsigned int i) const { return v[i]; }
 
-  void clear (unsigned char v = 0) { hb_memset (this, v, sizeof (*this)); }
+  void init0 ()
+  {
+    for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++)
+      v[i] = 0;
+  }
+  void init1 ()
+  {
+    for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++)
+      v[i] = (elt_t) -1;
+  }
 
   template <typename Op>
   hb_vector_size_t process (const Op& op) const
@@ -79,10 +89,10 @@ struct hb_vector_size_t
 
 struct hb_bit_page_t
 {
-  void init0 () { v.clear (); }
-  void init1 () { v.clear (0xFF); }
+  void init0 () { v.init0 (); }
+  void init1 () { v.init1 (); }
 
-  constexpr unsigned len () const
+  static inline constexpr unsigned len ()
   { return ARRAY_LENGTH_CONST (v); }
 
   bool is_empty () const
@@ -300,10 +310,10 @@ struct hb_bit_page_t
   static constexpr hb_codepoint_t INVALID = HB_SET_VALUE_INVALID;
 
   typedef unsigned long long elt_t;
-  static constexpr unsigned PAGE_BITS = 512;
-  static_assert ((PAGE_BITS & ((PAGE_BITS) - 1)) == 0, "");
-  static constexpr unsigned PAGE_BITS_LOG_2 = 9;
+  static constexpr unsigned PAGE_BITS_LOG_2 = 9; // 512 bits
+  static constexpr unsigned PAGE_BITS = 1 << PAGE_BITS_LOG_2;
   static_assert (1 << PAGE_BITS_LOG_2 == PAGE_BITS, "");
+  static_assert ((PAGE_BITS & ((PAGE_BITS) - 1)) == 0, "");
   static constexpr unsigned PAGE_BITMASK = PAGE_BITS - 1;
 
   static unsigned int elt_get_min (const elt_t &elt) { return hb_ctz (elt); }

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

@@ -74,6 +74,11 @@ struct hb_bit_set_invertible_t
       inverted = !inverted;
   }
 
+  bool is_inverted () const
+  {
+    return inverted;
+  }
+
   bool is_empty () const
   {
     hb_codepoint_t v = INVALID;

+ 11 - 7
thirdparty/harfbuzz/src/hb-bit-set.hh

@@ -38,7 +38,7 @@ struct hb_bit_set_t
   hb_bit_set_t () = default;
   ~hb_bit_set_t () = default;
 
-  hb_bit_set_t (const hb_bit_set_t& other) : hb_bit_set_t () { set (other); }
+  hb_bit_set_t (const hb_bit_set_t& other) : hb_bit_set_t () { set (other, true); }
   hb_bit_set_t ( hb_bit_set_t&& other) : hb_bit_set_t () { hb_swap (*this, other); }
   hb_bit_set_t& operator= (const hb_bit_set_t& other) { set (other); return *this; }
   hb_bit_set_t& operator= (hb_bit_set_t&& other) { hb_swap (*this, other); return *this; }
@@ -85,12 +85,16 @@ struct hb_bit_set_t
   void err () { if (successful) successful = false; } /* TODO Remove */
   bool in_error () const { return !successful; }
 
-  bool resize (unsigned int count, bool clear = true)
+  bool resize (unsigned int count, bool clear = true, bool exact_size = false)
   {
     if (unlikely (!successful)) return false;
-    if (unlikely (!pages.resize (count, clear) || !page_map.resize (count, clear)))
+
+    if (pages.length == 0 && count == 1)
+      exact_size = true; // Most sets are small and local
+
+    if (unlikely (!pages.resize (count, clear, exact_size) || !page_map.resize (count, clear, exact_size)))
     {
-      pages.resize (page_map.length);
+      pages.resize (page_map.length, clear, exact_size);
       successful = false;
       return false;
     }
@@ -346,11 +350,11 @@ struct hb_bit_set_t
     hb_codepoint_t c = first - 1;
     return next (&c) && c <= last;
   }
-  void set (const hb_bit_set_t &other)
+  void set (const hb_bit_set_t &other, bool exact_size = false)
   {
     if (unlikely (!successful)) return;
     unsigned int count = other.pages.length;
-    if (unlikely (!resize (count, false)))
+    if (unlikely (!resize (count, false, exact_size)))
       return;
     population = other.population;
 
@@ -422,7 +426,7 @@ struct hb_bit_set_t
   private:
   bool allocate_compact_workspace (hb_vector_t<unsigned>& workspace)
   {
-    if (unlikely (!workspace.resize (pages.length)))
+    if (unlikely (!workspace.resize_exact (pages.length)))
     {
       successful = false;
       return false;

+ 3 - 3
thirdparty/harfbuzz/src/hb-blob.cc

@@ -676,7 +676,7 @@ fail_without_close:
   wchar_t * wchar_file_name = (wchar_t *) hb_malloc (sizeof (wchar_t) * size);
   if (unlikely (!wchar_file_name)) goto fail_without_close;
   mbstowcs (wchar_file_name, file_name, size);
-#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
+#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)
   {
     CREATEFILE2_EXTENDED_PARAMETERS ceparams = { 0 };
     ceparams.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS);
@@ -697,7 +697,7 @@ fail_without_close:
 
   if (unlikely (fd == INVALID_HANDLE_VALUE)) goto fail_without_close;
 
-#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
+#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)
   {
     LARGE_INTEGER length;
     GetFileSizeEx (fd, &length);
@@ -710,7 +710,7 @@ fail_without_close:
 #endif
   if (unlikely (!file->mapping)) goto fail;
 
-#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
+#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)
   file->contents = (char *) MapViewOfFileFromApp (file->mapping, FILE_MAP_READ, 0, 0);
 #else
   file->contents = (char *) MapViewOfFile (file->mapping, FILE_MAP_READ, 0, 0, 0);

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

@@ -63,7 +63,7 @@ HB_BEGIN_DECLS
  *   HarfBuzz and doing that just once (no reuse!),
  *
  * - If the font is mmap()ed, it's okay to use
- *   @HB_MEMORY_READONLY_MAY_MAKE_WRITABLE, however, using that mode
+ *   @HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE, however, using that mode
  *   correctly is very tricky.  Use @HB_MEMORY_MODE_READONLY instead.
  **/
 typedef enum {

+ 185 - 187
thirdparty/harfbuzz/src/hb-buffer-deserialize-json.hh

@@ -35,34 +35,34 @@
 #line 33 "hb-buffer-deserialize-json.hh"
 static const unsigned char _deserialize_json_trans_keys[] = {
 	0u, 0u, 9u, 123u, 9u, 34u, 97u, 117u, 120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, 
-	48u, 57u, 9u, 125u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, 
-	9u, 125u, 9u, 125u, 108u, 108u, 34u, 34u, 9u, 58u, 9u, 57u, 9u, 125u, 9u, 125u, 
-	120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 34u, 34u, 
-	9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 108u, 108u, 34u, 34u, 9u, 58u, 
-	9u, 57u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 34u, 92u, 9u, 125u, 
-	34u, 92u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 9u, 125u, 9u, 93u, 
+	48u, 57u, 9u, 125u, 9u, 125u, 9u, 93u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 
+	48u, 57u, 9u, 125u, 9u, 125u, 108u, 108u, 34u, 34u, 9u, 58u, 9u, 57u, 9u, 125u, 
+	9u, 125u, 120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 
+	34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 108u, 108u, 34u, 34u, 
+	9u, 58u, 9u, 57u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 34u, 92u, 
+	9u, 125u, 34u, 92u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 9u, 125u, 
 	9u, 123u, 0u, 0u, 0
 };
 
 static const char _deserialize_json_key_spans[] = {
 	0, 115, 26, 21, 2, 1, 50, 49, 
-	10, 117, 117, 117, 1, 50, 49, 10, 
-	117, 117, 1, 1, 50, 49, 117, 117, 
-	2, 1, 50, 49, 10, 117, 117, 1, 
-	50, 49, 10, 117, 117, 1, 1, 50, 
-	49, 117, 117, 1, 50, 49, 59, 117, 
-	59, 117, 117, 1, 50, 49, 117, 85, 
+	10, 117, 117, 85, 117, 1, 50, 49, 
+	10, 117, 117, 1, 1, 50, 49, 117, 
+	117, 2, 1, 50, 49, 10, 117, 117, 
+	1, 50, 49, 10, 117, 117, 1, 1, 
+	50, 49, 117, 117, 1, 50, 49, 59, 
+	117, 59, 117, 117, 1, 50, 49, 117, 
 	115, 0
 };
 
 static const short _deserialize_json_index_offsets[] = {
 	0, 0, 116, 143, 165, 168, 170, 221, 
-	271, 282, 400, 518, 636, 638, 689, 739, 
-	750, 868, 986, 988, 990, 1041, 1091, 1209, 
-	1327, 1330, 1332, 1383, 1433, 1444, 1562, 1680, 
-	1682, 1733, 1783, 1794, 1912, 2030, 2032, 2034, 
-	2085, 2135, 2253, 2371, 2373, 2424, 2474, 2534, 
-	2652, 2712, 2830, 2948, 2950, 3001, 3051, 3169, 
+	271, 282, 400, 518, 604, 722, 724, 775, 
+	825, 836, 954, 1072, 1074, 1076, 1127, 1177, 
+	1295, 1413, 1416, 1418, 1469, 1519, 1530, 1648, 
+	1766, 1768, 1819, 1869, 1880, 1998, 2116, 2118, 
+	2120, 2171, 2221, 2339, 2457, 2459, 2510, 2560, 
+	2620, 2738, 2798, 2916, 3034, 3036, 3087, 3137, 
 	3255, 3371
 };
 
@@ -131,57 +131,54 @@ static const char _deserialize_json_indicies[] = {
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 24, 1, 20, 
-	20, 20, 20, 20, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 20, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 21, 1, 1, 1, 19, 19, 
-	19, 19, 19, 19, 19, 19, 19, 19, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 24, 1, 25, 
+	25, 25, 25, 25, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 25, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 26, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 22, 1, 25, 1, 25, 
-	25, 25, 25, 25, 1, 1, 1, 1, 
+	1, 1, 1, 27, 1, 20, 20, 20, 
+	20, 20, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 25, 1, 
+	1, 1, 1, 1, 20, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	21, 1, 1, 1, 19, 19, 19, 19, 
+	19, 19, 19, 19, 19, 19, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	26, 1, 26, 26, 26, 26, 26, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 26, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 27, 1, 
-	1, 28, 29, 29, 29, 29, 29, 29, 
-	29, 29, 29, 1, 30, 31, 31, 31, 
-	31, 31, 31, 31, 31, 31, 1, 32, 
-	32, 32, 32, 32, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 32, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 33, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 22, 1, 28, 1, 28, 28, 28, 
+	28, 28, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 28, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 29, 1, 
+	29, 29, 29, 29, 29, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 29, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 30, 1, 1, 31, 
+	32, 32, 32, 32, 32, 32, 32, 32, 
+	32, 1, 33, 34, 34, 34, 34, 34, 
+	34, 34, 34, 34, 1, 35, 35, 35, 
+	35, 35, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 35, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 34, 1, 32, 32, 32, 
-	32, 32, 1, 1, 1, 1, 1, 1, 
+	36, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 32, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	33, 1, 1, 1, 31, 31, 31, 31, 
-	31, 31, 31, 31, 31, 31, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
@@ -189,291 +186,294 @@ static const char _deserialize_json_indicies[] = {
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 37, 1, 35, 35, 35, 35, 35, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 34, 1, 35, 1, 36, 1, 36, 
-	36, 36, 36, 36, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 35, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 36, 1, 
+	1, 1, 34, 34, 34, 34, 34, 34, 
+	34, 34, 34, 34, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	37, 1, 37, 37, 37, 37, 37, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 37, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 38, 39, 39, 39, 39, 39, 39, 
-	39, 39, 39, 1, 40, 40, 40, 40, 
-	40, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 40, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 41, 
+	1, 1, 1, 1, 1, 1, 1, 37, 
+	1, 38, 1, 39, 1, 39, 39, 39, 
+	39, 39, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 39, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 40, 1, 
+	40, 40, 40, 40, 40, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 40, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 41, 
+	42, 42, 42, 42, 42, 42, 42, 42, 
+	42, 1, 43, 43, 43, 43, 43, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 43, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 44, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	42, 1, 40, 40, 40, 40, 40, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 40, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 41, 1, 1, 
-	1, 43, 43, 43, 43, 43, 43, 43, 
-	43, 43, 43, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 45, 1, 
+	43, 43, 43, 43, 43, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 43, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 44, 1, 1, 1, 46, 
+	46, 46, 46, 46, 46, 46, 46, 46, 
+	46, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 42, 1, 
-	44, 45, 1, 46, 1, 46, 46, 46, 
-	46, 46, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 46, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 47, 1, 
-	47, 47, 47, 47, 47, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 47, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 48, 1, 1, 49, 
-	50, 50, 50, 50, 50, 50, 50, 50, 
-	50, 1, 51, 52, 52, 52, 52, 52, 
-	52, 52, 52, 52, 1, 53, 53, 53, 
-	53, 53, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 53, 1, 1, 1, 
+	1, 1, 1, 1, 45, 1, 47, 48, 
+	1, 49, 1, 49, 49, 49, 49, 49, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	54, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 49, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 50, 1, 50, 50, 
+	50, 50, 50, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 50, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 51, 1, 1, 52, 53, 53, 
+	53, 53, 53, 53, 53, 53, 53, 1, 
+	54, 55, 55, 55, 55, 55, 55, 55, 
+	55, 55, 1, 56, 56, 56, 56, 56, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 56, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 57, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 55, 1, 53, 53, 53, 53, 53, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 53, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 54, 1, 
-	1, 1, 52, 52, 52, 52, 52, 52, 
-	52, 52, 52, 52, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 58, 
+	1, 56, 56, 56, 56, 56, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 55, 
-	1, 56, 1, 56, 56, 56, 56, 56, 
+	56, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 57, 1, 1, 1, 
+	55, 55, 55, 55, 55, 55, 55, 55, 
+	55, 55, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 56, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 57, 1, 57, 57, 
-	57, 57, 57, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 57, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 58, 1, 1, 59, 60, 60, 
-	60, 60, 60, 60, 60, 60, 60, 1, 
-	61, 62, 62, 62, 62, 62, 62, 62, 
-	62, 62, 1, 63, 63, 63, 63, 63, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 58, 1, 59, 
+	1, 59, 59, 59, 59, 59, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 63, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 64, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	59, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 60, 1, 60, 60, 60, 60, 
+	60, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 60, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	61, 1, 1, 62, 63, 63, 63, 63, 
+	63, 63, 63, 63, 63, 1, 64, 65, 
+	65, 65, 65, 65, 65, 65, 65, 65, 
+	1, 66, 66, 66, 66, 66, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	66, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 67, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 65, 
-	1, 63, 63, 63, 63, 63, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	63, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 64, 1, 1, 1, 
-	62, 62, 62, 62, 62, 62, 62, 62, 
-	62, 62, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 68, 1, 66, 
+	66, 66, 66, 66, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 66, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 65, 1, 66, 
-	1, 67, 1, 67, 67, 67, 67, 67, 
+	1, 1, 67, 1, 1, 1, 65, 65, 
+	65, 65, 65, 65, 65, 65, 65, 65, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 67, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 68, 1, 68, 68, 
-	68, 68, 68, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 68, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 69, 70, 70, 
-	70, 70, 70, 70, 70, 70, 70, 1, 
-	71, 71, 71, 71, 71, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 71, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 72, 1, 1, 1, 1, 
+	1, 1, 1, 68, 1, 69, 1, 70, 
+	1, 70, 70, 70, 70, 70, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	70, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 71, 1, 71, 71, 71, 71, 
+	71, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 71, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 72, 73, 73, 73, 73, 
+	73, 73, 73, 73, 73, 1, 74, 74, 
+	74, 74, 74, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 74, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 75, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 73, 1, 71, 71, 
-	71, 71, 71, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 71, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 72, 1, 1, 1, 74, 74, 74, 
-	74, 74, 74, 74, 74, 74, 74, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 76, 1, 74, 74, 74, 74, 
+	74, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 74, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 75, 
+	1, 1, 1, 77, 77, 77, 77, 77, 
+	77, 77, 77, 77, 77, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 73, 1, 75, 1, 75, 75, 
-	75, 75, 75, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 75, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 76, 
-	1, 76, 76, 76, 76, 76, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	76, 1, 77, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	78, 79, 79, 79, 79, 79, 79, 79, 
-	79, 79, 1, 81, 80, 80, 80, 80, 
-	80, 80, 80, 80, 80, 80, 80, 80, 
-	80, 80, 80, 80, 80, 80, 80, 80, 
-	80, 80, 80, 80, 80, 80, 80, 80, 
-	80, 80, 80, 80, 80, 80, 80, 80, 
-	80, 80, 80, 80, 80, 80, 80, 80, 
-	80, 80, 80, 80, 80, 80, 80, 80, 
-	80, 80, 80, 80, 80, 82, 80, 83, 
-	83, 83, 83, 83, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 83, 1, 
+	76, 1, 78, 1, 78, 78, 78, 78, 
+	78, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 84, 1, 1, 1, 1, 1, 
+	1, 1, 1, 78, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 79, 1, 79, 
+	79, 79, 79, 79, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 79, 1, 
+	80, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 81, 82, 
+	82, 82, 82, 82, 82, 82, 82, 82, 
+	1, 84, 83, 83, 83, 83, 83, 83, 
+	83, 83, 83, 83, 83, 83, 83, 83, 
+	83, 83, 83, 83, 83, 83, 83, 83, 
+	83, 83, 83, 83, 83, 83, 83, 83, 
+	83, 83, 83, 83, 83, 83, 83, 83, 
+	83, 83, 83, 83, 83, 83, 83, 83, 
+	83, 83, 83, 83, 83, 83, 83, 83, 
+	83, 83, 83, 85, 83, 86, 86, 86, 
+	86, 86, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 86, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	87, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 85, 1, 80, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 88, 1, 83, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 80, 
-	1, 86, 86, 86, 86, 86, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	86, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 87, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 83, 1, 89, 
+	89, 89, 89, 89, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 89, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 90, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 88, 1, 86, 
-	86, 86, 86, 86, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 86, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 87, 1, 1, 1, 89, 89, 
-	89, 89, 89, 89, 89, 89, 89, 89, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 91, 1, 89, 89, 89, 
+	89, 89, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 89, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	90, 1, 1, 1, 92, 92, 92, 92, 
+	92, 92, 92, 92, 92, 92, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 88, 1, 90, 1, 90, 
-	90, 90, 90, 90, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 90, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	91, 1, 91, 91, 91, 91, 91, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 91, 1, 93, 1, 93, 93, 93, 
+	93, 93, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 91, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 93, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 92, 93, 93, 93, 93, 93, 93, 
-	93, 93, 93, 1, 86, 86, 86, 86, 
-	86, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 86, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 87, 
-	1, 1, 1, 94, 94, 94, 94, 94, 
+	1, 1, 1, 1, 1, 1, 94, 1, 
 	94, 94, 94, 94, 94, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 94, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 95, 
+	96, 96, 96, 96, 96, 96, 96, 96, 
+	96, 1, 89, 89, 89, 89, 89, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 89, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 90, 1, 1, 
+	1, 97, 97, 97, 97, 97, 97, 97, 
+	97, 97, 97, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	88, 1, 95, 95, 95, 95, 95, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 95, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 96, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 97, 1, 
+	1, 1, 1, 1, 1, 1, 91, 1, 
 	0, 0, 0, 0, 0, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 0, 
@@ -492,39 +492,39 @@ static const char _deserialize_json_indicies[] = {
 };
 
 static const char _deserialize_json_trans_targs[] = {
-	1, 0, 2, 2, 3, 4, 18, 24, 
-	37, 43, 51, 5, 12, 6, 7, 8, 
-	9, 11, 9, 11, 10, 2, 55, 10, 
-	55, 13, 14, 15, 16, 17, 16, 17, 
-	10, 2, 55, 19, 20, 21, 22, 23, 
-	10, 2, 55, 23, 25, 31, 26, 27, 
-	28, 29, 30, 29, 30, 10, 2, 55, 
-	32, 33, 34, 35, 36, 35, 36, 10, 
-	2, 55, 38, 39, 40, 41, 42, 10, 
-	2, 55, 42, 44, 45, 46, 49, 50, 
-	46, 47, 48, 10, 2, 55, 10, 2, 
-	55, 50, 52, 53, 49, 54, 54, 55, 
-	56, 57
+	1, 0, 2, 2, 3, 4, 19, 25, 
+	38, 44, 52, 5, 13, 6, 7, 8, 
+	9, 12, 9, 12, 10, 2, 11, 10, 
+	11, 11, 56, 57, 14, 15, 16, 17, 
+	18, 17, 18, 10, 2, 11, 20, 21, 
+	22, 23, 24, 10, 2, 11, 24, 26, 
+	32, 27, 28, 29, 30, 31, 30, 31, 
+	10, 2, 11, 33, 34, 35, 36, 37, 
+	36, 37, 10, 2, 11, 39, 40, 41, 
+	42, 43, 10, 2, 11, 43, 45, 46, 
+	47, 50, 51, 47, 48, 49, 10, 2, 
+	11, 10, 2, 11, 51, 53, 54, 50, 
+	55, 55
 };
 
 static const char _deserialize_json_trans_actions[] = {
 	0, 0, 1, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 2, 
 	2, 2, 0, 0, 3, 3, 4, 0, 
-	5, 0, 0, 2, 2, 2, 0, 0, 
-	6, 6, 7, 0, 0, 0, 2, 2, 
-	8, 8, 9, 0, 0, 0, 0, 0, 
-	2, 2, 2, 0, 0, 10, 10, 11, 
-	0, 0, 2, 2, 2, 0, 0, 12, 
-	12, 13, 0, 0, 0, 2, 2, 14, 
-	14, 15, 0, 0, 0, 2, 16, 16, 
-	0, 17, 0, 18, 18, 19, 20, 20, 
-	21, 17, 0, 0, 22, 22, 23, 0, 
-	0, 0
+	5, 0, 0, 0, 0, 0, 2, 2, 
+	2, 0, 0, 6, 6, 7, 0, 0, 
+	0, 2, 2, 8, 8, 9, 0, 0, 
+	0, 0, 0, 2, 2, 2, 0, 0, 
+	10, 10, 11, 0, 0, 2, 2, 2, 
+	0, 0, 12, 12, 13, 0, 0, 0, 
+	2, 2, 14, 14, 15, 0, 0, 0, 
+	2, 16, 16, 0, 17, 0, 18, 18, 
+	19, 20, 20, 21, 17, 0, 0, 22, 
+	22, 23
 };
 
 static const int deserialize_json_start = 1;
-static const int deserialize_json_first_final = 55;
+static const int deserialize_json_first_final = 56;
 static const int deserialize_json_error = 0;
 
 static const int deserialize_json_en_main = 1;
@@ -548,21 +548,19 @@ _hb_buffer_deserialize_json (hb_buffer_t *buffer,
   while (p < pe && ISSPACE (*p))
     p++;
   if (p < pe && *p == (buffer->len ? ',' : '['))
-  {
     *end_ptr = ++p;
-  }
 
   const char *tok = nullptr;
   int cs;
   hb_glyph_info_t info = {0};
   hb_glyph_position_t pos = {0};
   
-#line 554 "hb-buffer-deserialize-json.hh"
+#line 552 "hb-buffer-deserialize-json.hh"
 	{
 	cs = deserialize_json_start;
 	}
 
-#line 557 "hb-buffer-deserialize-json.hh"
+#line 555 "hb-buffer-deserialize-json.hh"
 	{
 	int _slen;
 	int _trans;
@@ -774,7 +772,7 @@ _resume:
 	*end_ptr = p;
 }
 	break;
-#line 735 "hb-buffer-deserialize-json.hh"
+#line 733 "hb-buffer-deserialize-json.hh"
 	}
 
 _again:
@@ -786,7 +784,7 @@ _again:
 	_out: {}
 	}
 
-#line 139 "hb-buffer-deserialize-json.rl"
+#line 137 "hb-buffer-deserialize-json.rl"
 
 
   *end_ptr = p;

+ 692 - 0
thirdparty/harfbuzz/src/hb-buffer-deserialize-text-glyphs.hh

@@ -0,0 +1,692 @@
+
+#line 1 "hb-buffer-deserialize-text-glyphs.rl"
+/*
+ * Copyright © 2013  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): Behdad Esfahbod
+ */
+
+#ifndef HB_BUFFER_DESERIALIZE_TEXT_GLYPHS_HH
+#define HB_BUFFER_DESERIALIZE_TEXT_GLYPHS_HH
+
+#include "hb.hh"
+
+
+#line 33 "hb-buffer-deserialize-text-glyphs.hh"
+static const unsigned char _deserialize_text_glyphs_trans_keys[] = {
+	0u, 0u, 48u, 57u, 45u, 57u, 48u, 57u, 45u, 57u, 48u, 57u, 48u, 57u, 45u, 57u, 
+	48u, 57u, 44u, 44u, 45u, 57u, 48u, 57u, 44u, 57u, 43u, 124u, 9u, 124u, 9u, 124u, 
+	9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 
+	9u, 124u, 9u, 124u, 9u, 124u, 0
+};
+
+static const char _deserialize_text_glyphs_key_spans[] = {
+	0, 10, 13, 10, 13, 10, 10, 13, 
+	10, 1, 13, 10, 14, 82, 116, 116, 
+	116, 116, 116, 116, 116, 116, 116, 116, 
+	116, 116, 116
+};
+
+static const short _deserialize_text_glyphs_index_offsets[] = {
+	0, 0, 11, 25, 36, 50, 61, 72, 
+	86, 97, 99, 113, 124, 139, 222, 339, 
+	456, 573, 690, 807, 924, 1041, 1158, 1275, 
+	1392, 1509, 1626
+};
+
+static const char _deserialize_text_glyphs_indicies[] = {
+	0, 2, 2, 2, 2, 2, 2, 
+	2, 2, 2, 1, 3, 1, 1, 4, 
+	5, 5, 5, 5, 5, 5, 5, 5, 
+	5, 1, 6, 7, 7, 7, 7, 7, 
+	7, 7, 7, 7, 1, 8, 1, 1, 
+	9, 10, 10, 10, 10, 10, 10, 10, 
+	10, 10, 1, 11, 12, 12, 12, 12, 
+	12, 12, 12, 12, 12, 1, 13, 14, 
+	14, 14, 14, 14, 14, 14, 14, 14, 
+	1, 15, 1, 1, 16, 17, 17, 17, 
+	17, 17, 17, 17, 17, 17, 1, 18, 
+	19, 19, 19, 19, 19, 19, 19, 19, 
+	19, 1, 20, 1, 21, 1, 1, 22, 
+	23, 23, 23, 23, 23, 23, 23, 23, 
+	23, 1, 24, 25, 25, 25, 25, 25, 
+	25, 25, 25, 25, 1, 20, 1, 1, 
+	1, 19, 19, 19, 19, 19, 19, 19, 
+	19, 19, 19, 1, 26, 26, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 26, 1, 
+	1, 26, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 26, 26, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 26, 1, 28, 
+	28, 28, 28, 28, 27, 27, 27, 27, 
+	27, 27, 27, 27, 27, 27, 27, 27, 
+	27, 27, 27, 27, 27, 27, 28, 27, 
+	27, 29, 27, 27, 27, 27, 27, 27, 
+	27, 30, 1, 27, 27, 27, 27, 27, 
+	27, 27, 27, 27, 27, 27, 27, 27, 
+	27, 27, 27, 31, 27, 27, 32, 27, 
+	27, 27, 27, 27, 27, 27, 27, 27, 
+	27, 27, 27, 27, 27, 27, 27, 27, 
+	27, 27, 27, 27, 27, 27, 27, 27, 
+	27, 27, 33, 1, 27, 27, 27, 27, 
+	27, 27, 27, 27, 27, 27, 27, 27, 
+	27, 27, 27, 27, 27, 27, 27, 27, 
+	27, 27, 27, 27, 27, 27, 27, 27, 
+	27, 27, 28, 27, 34, 34, 34, 34, 
+	34, 26, 26, 26, 26, 26, 26, 26, 
+	26, 26, 26, 26, 26, 26, 26, 26, 
+	26, 26, 26, 34, 26, 26, 35, 26, 
+	26, 26, 26, 26, 26, 26, 36, 1, 
+	26, 26, 26, 26, 26, 26, 26, 26, 
+	26, 26, 26, 26, 26, 26, 26, 26, 
+	37, 26, 26, 38, 26, 26, 26, 26, 
+	26, 26, 26, 26, 26, 26, 26, 26, 
+	26, 26, 26, 26, 26, 26, 26, 26, 
+	26, 26, 26, 26, 26, 26, 26, 39, 
+	1, 26, 26, 26, 26, 26, 26, 26, 
+	26, 26, 26, 26, 26, 26, 26, 26, 
+	26, 26, 26, 26, 26, 26, 26, 26, 
+	26, 26, 26, 26, 26, 26, 26, 40, 
+	26, 41, 41, 41, 41, 41, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	41, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 42, 1, 43, 43, 
+	43, 43, 43, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 43, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 44, 1, 41, 41, 41, 41, 41, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 41, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 45, 45, 45, 45, 45, 45, 
+	45, 45, 45, 45, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 42, 1, 
+	46, 46, 46, 46, 46, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 46, 
+	1, 1, 47, 1, 1, 1, 1, 1, 
+	1, 1, 1, 48, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 49, 1, 50, 50, 50, 
+	50, 50, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 50, 1, 1, 51, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	52, 1, 50, 50, 50, 50, 50, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 50, 1, 1, 51, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 12, 12, 12, 12, 12, 12, 12, 
+	12, 12, 12, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 52, 1, 46, 
+	46, 46, 46, 46, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 46, 1, 
+	1, 47, 1, 1, 1, 1, 1, 1, 
+	1, 1, 48, 1, 1, 1, 7, 7, 
+	7, 7, 7, 7, 7, 7, 7, 7, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 49, 1, 53, 53, 53, 53, 
+	53, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 53, 1, 1, 54, 1, 
+	1, 1, 1, 1, 1, 1, 55, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 56, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 57, 
+	1, 58, 58, 58, 58, 58, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	58, 1, 1, 59, 1, 1, 1, 1, 
+	1, 1, 1, 60, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 61, 1, 58, 58, 
+	58, 58, 58, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 58, 1, 1, 
+	59, 1, 1, 1, 1, 1, 1, 1, 
+	60, 1, 1, 1, 1, 25, 25, 25, 
+	25, 25, 25, 25, 25, 25, 25, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 61, 1, 53, 53, 53, 53, 53, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 53, 1, 1, 54, 1, 1, 
+	1, 1, 1, 1, 1, 55, 1, 1, 
+	1, 1, 62, 62, 62, 62, 62, 62, 
+	62, 62, 62, 62, 1, 1, 1, 1, 
+	1, 1, 56, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 57, 1, 
+	0
+};
+
+static const char _deserialize_text_glyphs_trans_targs[] = {
+	16, 0, 18, 3, 19, 22, 19, 22, 
+	5, 20, 21, 20, 21, 23, 26, 8, 
+	9, 12, 9, 12, 10, 11, 24, 25, 
+	24, 25, 15, 15, 14, 1, 2, 6, 
+	7, 13, 15, 1, 2, 6, 7, 13, 
+	14, 17, 14, 17, 14, 18, 17, 1, 
+	4, 14, 17, 1, 14, 17, 1, 2, 
+	7, 14, 17, 1, 2, 14, 26
+};
+
+static const char _deserialize_text_glyphs_trans_actions[] = {
+	1, 0, 1, 1, 1, 1, 0, 0, 
+	1, 1, 1, 0, 0, 1, 1, 1, 
+	1, 1, 0, 0, 2, 1, 1, 1, 
+	0, 0, 0, 4, 3, 5, 5, 5, 
+	5, 4, 6, 7, 7, 7, 7, 0, 
+	6, 8, 8, 0, 0, 0, 9, 10, 
+	10, 9, 11, 12, 11, 13, 14, 14, 
+	14, 13, 15, 16, 16, 15, 0
+};
+
+static const char _deserialize_text_glyphs_eof_actions[] = {
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 3, 6, 
+	8, 0, 8, 9, 11, 11, 9, 13, 
+	15, 15, 13
+};
+
+static const int deserialize_text_glyphs_start = 14;
+static const int deserialize_text_glyphs_first_final = 14;
+static const int deserialize_text_glyphs_error = 0;
+
+static const int deserialize_text_glyphs_en_main = 14;
+
+
+#line 98 "hb-buffer-deserialize-text-glyphs.rl"
+
+
+static hb_bool_t
+_hb_buffer_deserialize_text_glyphs (hb_buffer_t *buffer,
+				    const char *buf,
+				    unsigned int buf_len,
+				    const char **end_ptr,
+				    hb_font_t *font)
+{
+  const char *p = buf, *pe = buf + buf_len, *eof = pe, *orig_pe = pe;
+
+  /* Ensure we have positions. */
+  (void) hb_buffer_get_glyph_positions (buffer, nullptr);
+
+  while (p < pe && ISSPACE (*p))
+    p++;
+  if (p < pe && *p == (buffer->len ? '|' : '['))
+    *end_ptr = ++p;
+
+  const char *end = strchr ((char *) p, ']');
+  if (end)
+    pe = eof = end;
+  else
+  {
+    end = strrchr ((char *) p, '|');
+    if (end)
+      pe = eof = end;
+    else
+      pe = eof = p;
+  }
+
+  const char *tok = nullptr;
+  int cs;
+  hb_glyph_info_t info = {0};
+  hb_glyph_position_t pos = {0};
+  
+#line 346 "hb-buffer-deserialize-text-glyphs.hh"
+	{
+	cs = deserialize_text_glyphs_start;
+	}
+
+#line 349 "hb-buffer-deserialize-text-glyphs.hh"
+	{
+	int _slen;
+	int _trans;
+	const unsigned char *_keys;
+	const char *_inds;
+	if ( p == pe )
+		goto _test_eof;
+	if ( cs == 0 )
+		goto _out;
+_resume:
+	_keys = _deserialize_text_glyphs_trans_keys + (cs<<1);
+	_inds = _deserialize_text_glyphs_indicies + _deserialize_text_glyphs_index_offsets[cs];
+
+	_slen = _deserialize_text_glyphs_key_spans[cs];
+	_trans = _inds[ _slen > 0 && _keys[0] <=(*p) &&
+		(*p) <= _keys[1] ?
+		(*p) - _keys[0] : _slen ];
+
+	cs = _deserialize_text_glyphs_trans_targs[_trans];
+
+	if ( _deserialize_text_glyphs_trans_actions[_trans] == 0 )
+		goto _again;
+
+	switch ( _deserialize_text_glyphs_trans_actions[_trans] ) {
+	case 1:
+#line 51 "hb-buffer-deserialize-text-glyphs.rl"
+	{
+	tok = p;
+}
+	break;
+	case 7:
+#line 55 "hb-buffer-deserialize-text-glyphs.rl"
+	{
+	/* TODO Unescape delimiters. */
+	if (!hb_font_glyph_from_string (font,
+					tok, p - tok,
+					&info.codepoint))
+	  return false;
+}
+	break;
+	case 14:
+#line 63 "hb-buffer-deserialize-text-glyphs.rl"
+	{ if (!parse_uint (tok, p, &info.cluster )) return false; }
+	break;
+	case 2:
+#line 64 "hb-buffer-deserialize-text-glyphs.rl"
+	{ if (!parse_int  (tok, p, &pos.x_offset )) return false; }
+	break;
+	case 16:
+#line 65 "hb-buffer-deserialize-text-glyphs.rl"
+	{ if (!parse_int  (tok, p, &pos.y_offset )) return false; }
+	break;
+	case 10:
+#line 66 "hb-buffer-deserialize-text-glyphs.rl"
+	{ if (!parse_int  (tok, p, &pos.x_advance)) return false; }
+	break;
+	case 12:
+#line 67 "hb-buffer-deserialize-text-glyphs.rl"
+	{ if (!parse_int  (tok, p, &pos.y_advance)) return false; }
+	break;
+	case 4:
+#line 38 "hb-buffer-deserialize-text-glyphs.rl"
+	{
+	hb_memset (&info, 0, sizeof (info));
+	hb_memset (&pos , 0, sizeof (pos ));
+}
+#line 51 "hb-buffer-deserialize-text-glyphs.rl"
+	{
+	tok = p;
+}
+	break;
+	case 6:
+#line 55 "hb-buffer-deserialize-text-glyphs.rl"
+	{
+	/* TODO Unescape delimiters. */
+	if (!hb_font_glyph_from_string (font,
+					tok, p - tok,
+					&info.codepoint))
+	  return false;
+}
+#line 43 "hb-buffer-deserialize-text-glyphs.rl"
+	{
+	buffer->add_info (info);
+	if (unlikely (!buffer->successful))
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+	case 13:
+#line 63 "hb-buffer-deserialize-text-glyphs.rl"
+	{ if (!parse_uint (tok, p, &info.cluster )) return false; }
+#line 43 "hb-buffer-deserialize-text-glyphs.rl"
+	{
+	buffer->add_info (info);
+	if (unlikely (!buffer->successful))
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+	case 15:
+#line 65 "hb-buffer-deserialize-text-glyphs.rl"
+	{ if (!parse_int  (tok, p, &pos.y_offset )) return false; }
+#line 43 "hb-buffer-deserialize-text-glyphs.rl"
+	{
+	buffer->add_info (info);
+	if (unlikely (!buffer->successful))
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+	case 9:
+#line 66 "hb-buffer-deserialize-text-glyphs.rl"
+	{ if (!parse_int  (tok, p, &pos.x_advance)) return false; }
+#line 43 "hb-buffer-deserialize-text-glyphs.rl"
+	{
+	buffer->add_info (info);
+	if (unlikely (!buffer->successful))
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+	case 11:
+#line 67 "hb-buffer-deserialize-text-glyphs.rl"
+	{ if (!parse_int  (tok, p, &pos.y_advance)) return false; }
+#line 43 "hb-buffer-deserialize-text-glyphs.rl"
+	{
+	buffer->add_info (info);
+	if (unlikely (!buffer->successful))
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+	case 8:
+#line 68 "hb-buffer-deserialize-text-glyphs.rl"
+	{ if (!parse_uint (tok, p, &info.mask    )) return false; }
+#line 43 "hb-buffer-deserialize-text-glyphs.rl"
+	{
+	buffer->add_info (info);
+	if (unlikely (!buffer->successful))
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+	case 5:
+#line 38 "hb-buffer-deserialize-text-glyphs.rl"
+	{
+	hb_memset (&info, 0, sizeof (info));
+	hb_memset (&pos , 0, sizeof (pos ));
+}
+#line 51 "hb-buffer-deserialize-text-glyphs.rl"
+	{
+	tok = p;
+}
+#line 55 "hb-buffer-deserialize-text-glyphs.rl"
+	{
+	/* TODO Unescape delimiters. */
+	if (!hb_font_glyph_from_string (font,
+					tok, p - tok,
+					&info.codepoint))
+	  return false;
+}
+	break;
+	case 3:
+#line 38 "hb-buffer-deserialize-text-glyphs.rl"
+	{
+	hb_memset (&info, 0, sizeof (info));
+	hb_memset (&pos , 0, sizeof (pos ));
+}
+#line 51 "hb-buffer-deserialize-text-glyphs.rl"
+	{
+	tok = p;
+}
+#line 55 "hb-buffer-deserialize-text-glyphs.rl"
+	{
+	/* TODO Unescape delimiters. */
+	if (!hb_font_glyph_from_string (font,
+					tok, p - tok,
+					&info.codepoint))
+	  return false;
+}
+#line 43 "hb-buffer-deserialize-text-glyphs.rl"
+	{
+	buffer->add_info (info);
+	if (unlikely (!buffer->successful))
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+#line 516 "hb-buffer-deserialize-text-glyphs.hh"
+	}
+
+_again:
+	if ( cs == 0 )
+		goto _out;
+	if ( ++p != pe )
+		goto _resume;
+	_test_eof: {}
+	if ( p == eof )
+	{
+	switch ( _deserialize_text_glyphs_eof_actions[cs] ) {
+	case 6:
+#line 55 "hb-buffer-deserialize-text-glyphs.rl"
+	{
+	/* TODO Unescape delimiters. */
+	if (!hb_font_glyph_from_string (font,
+					tok, p - tok,
+					&info.codepoint))
+	  return false;
+}
+#line 43 "hb-buffer-deserialize-text-glyphs.rl"
+	{
+	buffer->add_info (info);
+	if (unlikely (!buffer->successful))
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+	case 13:
+#line 63 "hb-buffer-deserialize-text-glyphs.rl"
+	{ if (!parse_uint (tok, p, &info.cluster )) return false; }
+#line 43 "hb-buffer-deserialize-text-glyphs.rl"
+	{
+	buffer->add_info (info);
+	if (unlikely (!buffer->successful))
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+	case 15:
+#line 65 "hb-buffer-deserialize-text-glyphs.rl"
+	{ if (!parse_int  (tok, p, &pos.y_offset )) return false; }
+#line 43 "hb-buffer-deserialize-text-glyphs.rl"
+	{
+	buffer->add_info (info);
+	if (unlikely (!buffer->successful))
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+	case 9:
+#line 66 "hb-buffer-deserialize-text-glyphs.rl"
+	{ if (!parse_int  (tok, p, &pos.x_advance)) return false; }
+#line 43 "hb-buffer-deserialize-text-glyphs.rl"
+	{
+	buffer->add_info (info);
+	if (unlikely (!buffer->successful))
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+	case 11:
+#line 67 "hb-buffer-deserialize-text-glyphs.rl"
+	{ if (!parse_int  (tok, p, &pos.y_advance)) return false; }
+#line 43 "hb-buffer-deserialize-text-glyphs.rl"
+	{
+	buffer->add_info (info);
+	if (unlikely (!buffer->successful))
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+	case 8:
+#line 68 "hb-buffer-deserialize-text-glyphs.rl"
+	{ if (!parse_uint (tok, p, &info.mask    )) return false; }
+#line 43 "hb-buffer-deserialize-text-glyphs.rl"
+	{
+	buffer->add_info (info);
+	if (unlikely (!buffer->successful))
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+	case 3:
+#line 38 "hb-buffer-deserialize-text-glyphs.rl"
+	{
+	hb_memset (&info, 0, sizeof (info));
+	hb_memset (&pos , 0, sizeof (pos ));
+}
+#line 51 "hb-buffer-deserialize-text-glyphs.rl"
+	{
+	tok = p;
+}
+#line 55 "hb-buffer-deserialize-text-glyphs.rl"
+	{
+	/* TODO Unescape delimiters. */
+	if (!hb_font_glyph_from_string (font,
+					tok, p - tok,
+					&info.codepoint))
+	  return false;
+}
+#line 43 "hb-buffer-deserialize-text-glyphs.rl"
+	{
+	buffer->add_info (info);
+	if (unlikely (!buffer->successful))
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+#line 616 "hb-buffer-deserialize-text-glyphs.hh"
+	}
+	}
+
+	_out: {}
+	}
+
+#line 136 "hb-buffer-deserialize-text-glyphs.rl"
+
+
+  if (pe < orig_pe && *pe == ']')
+  {
+    pe++;
+    if (p == pe)
+      p++;
+  }
+
+  *end_ptr = p;
+
+  return p == pe;
+}
+
+#endif /* HB_BUFFER_DESERIALIZE_TEXT_GLYPHS_HH */

+ 332 - 0
thirdparty/harfbuzz/src/hb-buffer-deserialize-text-unicode.hh

@@ -0,0 +1,332 @@
+
+#line 1 "hb-buffer-deserialize-text-unicode.rl"
+/*
+ * Copyright © 2013  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): Behdad Esfahbod
+ */
+
+#ifndef HB_BUFFER_DESERIALIZE_TEXT_UNICODE_HH
+#define HB_BUFFER_DESERIALIZE_TEXT_UNICODE_HH
+
+#include "hb.hh"
+
+
+#line 33 "hb-buffer-deserialize-text-unicode.hh"
+static const unsigned char _deserialize_text_unicode_trans_keys[] = {
+	0u, 0u, 9u, 117u, 43u, 102u, 48u, 102u, 48u, 57u, 9u, 124u, 9u, 124u, 9u, 124u, 
+	9u, 124u, 0
+};
+
+static const char _deserialize_text_unicode_key_spans[] = {
+	0, 109, 60, 55, 10, 116, 116, 116, 
+	116
+};
+
+static const short _deserialize_text_unicode_index_offsets[] = {
+	0, 0, 110, 171, 227, 238, 355, 472, 
+	589
+};
+
+static const char _deserialize_text_unicode_indicies[] = {
+	0, 0, 0, 0, 0, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	0, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 2, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 2, 1, 3, 
+	1, 1, 1, 1, 4, 4, 4, 4, 
+	4, 4, 4, 4, 4, 4, 1, 1, 
+	1, 1, 1, 1, 1, 4, 4, 4, 
+	4, 4, 4, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 4, 4, 4, 
+	4, 4, 4, 1, 4, 4, 4, 4, 
+	4, 4, 4, 4, 4, 4, 1, 1, 
+	1, 1, 1, 1, 1, 4, 4, 4, 
+	4, 4, 4, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 4, 4, 4, 
+	4, 4, 4, 1, 5, 6, 6, 6, 
+	6, 6, 6, 6, 6, 6, 1, 7, 
+	7, 7, 7, 7, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 7, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 8, 8, 
+	8, 8, 8, 8, 8, 8, 8, 8, 
+	1, 1, 1, 9, 1, 1, 1, 8, 
+	8, 8, 8, 8, 8, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 8, 
+	8, 8, 8, 8, 8, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 10, 1, 11, 11, 11, 11, 
+	11, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 11, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 0, 
+	1, 12, 12, 12, 12, 12, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	12, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 13, 1, 12, 12, 
+	12, 12, 12, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 12, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 14, 14, 14, 
+	14, 14, 14, 14, 14, 14, 14, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 13, 1, 0
+};
+
+static const char _deserialize_text_unicode_trans_targs[] = {
+	1, 0, 2, 3, 5, 7, 8, 6, 
+	5, 4, 1, 6, 6, 1, 8
+};
+
+static const char _deserialize_text_unicode_trans_actions[] = {
+	0, 0, 1, 0, 2, 2, 2, 3, 
+	0, 4, 3, 0, 5, 5, 0
+};
+
+static const char _deserialize_text_unicode_eof_actions[] = {
+	0, 0, 0, 0, 0, 3, 0, 5, 
+	5
+};
+
+static const int deserialize_text_unicode_start = 1;
+static const int deserialize_text_unicode_first_final = 5;
+static const int deserialize_text_unicode_error = 0;
+
+static const int deserialize_text_unicode_en_main = 1;
+
+
+#line 79 "hb-buffer-deserialize-text-unicode.rl"
+
+
+static hb_bool_t
+_hb_buffer_deserialize_text_unicode (hb_buffer_t *buffer,
+				     const char *buf,
+				     unsigned int buf_len,
+				     const char **end_ptr,
+				     hb_font_t *font)
+{
+  const char *p = buf, *pe = buf + buf_len, *eof = pe, *orig_pe = pe;
+
+  while (p < pe && ISSPACE (*p))
+    p++;
+  if (p < pe && *p == (buffer->len ? '|' : '<'))
+    *end_ptr = ++p;
+
+  const char *end = strchr ((char *) p, '>');
+  if (end)
+    pe = eof = end;
+  else
+  {
+    end = strrchr ((char *) p, '|');
+    if (end)
+      pe = eof = end;
+    else
+      pe = eof = p;
+  }
+
+
+  const char *tok = nullptr;
+  int cs;
+  hb_glyph_info_t info = {0};
+  const hb_glyph_position_t pos = {0};
+  
+#line 194 "hb-buffer-deserialize-text-unicode.hh"
+	{
+	cs = deserialize_text_unicode_start;
+	}
+
+#line 197 "hb-buffer-deserialize-text-unicode.hh"
+	{
+	int _slen;
+	int _trans;
+	const unsigned char *_keys;
+	const char *_inds;
+	if ( p == pe )
+		goto _test_eof;
+	if ( cs == 0 )
+		goto _out;
+_resume:
+	_keys = _deserialize_text_unicode_trans_keys + (cs<<1);
+	_inds = _deserialize_text_unicode_indicies + _deserialize_text_unicode_index_offsets[cs];
+
+	_slen = _deserialize_text_unicode_key_spans[cs];
+	_trans = _inds[ _slen > 0 && _keys[0] <=(*p) &&
+		(*p) <= _keys[1] ?
+		(*p) - _keys[0] : _slen ];
+
+	cs = _deserialize_text_unicode_trans_targs[_trans];
+
+	if ( _deserialize_text_unicode_trans_actions[_trans] == 0 )
+		goto _again;
+
+	switch ( _deserialize_text_unicode_trans_actions[_trans] ) {
+	case 1:
+#line 38 "hb-buffer-deserialize-text-unicode.rl"
+	{
+	hb_memset (&info, 0, sizeof (info));
+}
+	break;
+	case 2:
+#line 51 "hb-buffer-deserialize-text-unicode.rl"
+	{
+	tok = p;
+}
+	break;
+	case 4:
+#line 55 "hb-buffer-deserialize-text-unicode.rl"
+	{if (!parse_hex (tok, p, &info.codepoint )) return false; }
+	break;
+	case 3:
+#line 55 "hb-buffer-deserialize-text-unicode.rl"
+	{if (!parse_hex (tok, p, &info.codepoint )) return false; }
+#line 42 "hb-buffer-deserialize-text-unicode.rl"
+	{
+	buffer->add_info (info);
+	if (unlikely (!buffer->successful))
+	  return false;
+	if (buffer->have_positions)
+	  buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+	case 5:
+#line 57 "hb-buffer-deserialize-text-unicode.rl"
+	{ if (!parse_uint (tok, p, &info.cluster )) return false; }
+#line 42 "hb-buffer-deserialize-text-unicode.rl"
+	{
+	buffer->add_info (info);
+	if (unlikely (!buffer->successful))
+	  return false;
+	if (buffer->have_positions)
+	  buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+#line 256 "hb-buffer-deserialize-text-unicode.hh"
+	}
+
+_again:
+	if ( cs == 0 )
+		goto _out;
+	if ( ++p != pe )
+		goto _resume;
+	_test_eof: {}
+	if ( p == eof )
+	{
+	switch ( _deserialize_text_unicode_eof_actions[cs] ) {
+	case 3:
+#line 55 "hb-buffer-deserialize-text-unicode.rl"
+	{if (!parse_hex (tok, p, &info.codepoint )) return false; }
+#line 42 "hb-buffer-deserialize-text-unicode.rl"
+	{
+	buffer->add_info (info);
+	if (unlikely (!buffer->successful))
+	  return false;
+	if (buffer->have_positions)
+	  buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+	case 5:
+#line 57 "hb-buffer-deserialize-text-unicode.rl"
+	{ if (!parse_uint (tok, p, &info.cluster )) return false; }
+#line 42 "hb-buffer-deserialize-text-unicode.rl"
+	{
+	buffer->add_info (info);
+	if (unlikely (!buffer->successful))
+	  return false;
+	if (buffer->have_positions)
+	  buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+#line 289 "hb-buffer-deserialize-text-unicode.hh"
+	}
+	}
+
+	_out: {}
+	}
+
+#line 115 "hb-buffer-deserialize-text-unicode.rl"
+
+
+  if (pe < orig_pe && *pe == '>')
+  {
+    pe++;
+    if (p == pe)
+      p++;
+  }
+
+  *end_ptr = p;
+
+  return p == pe;
+}
+
+#endif /* HB_BUFFER_DESERIALIZE_TEXT_UNICODE_HH */

+ 0 - 917
thirdparty/harfbuzz/src/hb-buffer-deserialize-text.hh

@@ -1,917 +0,0 @@
-
-#line 1 "hb-buffer-deserialize-text.rl"
-/*
- * Copyright © 2013  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): Behdad Esfahbod
- */
-
-#ifndef HB_BUFFER_DESERIALIZE_TEXT_HH
-#define HB_BUFFER_DESERIALIZE_TEXT_HH
-
-#include "hb.hh"
-
-
-#line 33 "hb-buffer-deserialize-text.hh"
-static const unsigned char _deserialize_text_trans_keys[] = {
-	0u, 0u, 9u, 91u, 85u, 85u, 43u, 43u, 48u, 102u, 9u, 85u, 48u, 57u, 48u, 57u, 
-	45u, 57u, 48u, 57u, 45u, 57u, 48u, 57u, 48u, 57u, 45u, 57u, 48u, 57u, 44u, 44u, 
-	45u, 57u, 48u, 57u, 44u, 57u, 43u, 124u, 9u, 124u, 9u, 124u, 0u, 0u, 9u, 85u, 
-	9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 
-	9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 
-	0
-};
-
-static const char _deserialize_text_key_spans[] = {
-	0, 83, 1, 1, 55, 77, 10, 10, 
-	13, 10, 13, 10, 10, 13, 10, 1, 
-	13, 10, 14, 82, 116, 116, 0, 77, 
-	116, 116, 116, 116, 116, 116, 116, 116, 
-	116, 116, 116, 116, 116, 116, 116, 116
-};
-
-static const short _deserialize_text_index_offsets[] = {
-	0, 0, 84, 86, 88, 144, 222, 233, 
-	244, 258, 269, 283, 294, 305, 319, 330, 
-	332, 346, 357, 372, 455, 572, 689, 690, 
-	768, 885, 1002, 1119, 1236, 1353, 1470, 1587, 
-	1704, 1821, 1938, 2055, 2172, 2289, 2406, 2523
-};
-
-static const char _deserialize_text_indicies[] = {
-	0, 0, 0, 0, 0, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	0, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 2, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 3, 1, 4, 1, 5, 
-	1, 6, 6, 6, 6, 6, 6, 6, 
-	6, 6, 6, 1, 1, 1, 1, 1, 
-	1, 1, 6, 6, 6, 6, 6, 6, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 6, 6, 6, 6, 6, 6, 
-	1, 7, 7, 7, 7, 7, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	7, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 4, 1, 8, 
-	9, 9, 9, 9, 9, 9, 9, 9, 
-	9, 1, 10, 11, 11, 11, 11, 11, 
-	11, 11, 11, 11, 1, 12, 1, 1, 
-	13, 14, 14, 14, 14, 14, 14, 14, 
-	14, 14, 1, 15, 16, 16, 16, 16, 
-	16, 16, 16, 16, 16, 1, 17, 1, 
-	1, 18, 19, 19, 19, 19, 19, 19, 
-	19, 19, 19, 1, 20, 21, 21, 21, 
-	21, 21, 21, 21, 21, 21, 1, 22, 
-	23, 23, 23, 23, 23, 23, 23, 23, 
-	23, 1, 24, 1, 1, 25, 26, 26, 
-	26, 26, 26, 26, 26, 26, 26, 1, 
-	27, 28, 28, 28, 28, 28, 28, 28, 
-	28, 28, 1, 29, 1, 30, 1, 1, 
-	31, 32, 32, 32, 32, 32, 32, 32, 
-	32, 32, 1, 33, 34, 34, 34, 34, 
-	34, 34, 34, 34, 34, 1, 29, 1, 
-	1, 1, 28, 28, 28, 28, 28, 28, 
-	28, 28, 28, 28, 1, 35, 35, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 35, 
-	1, 1, 35, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 35, 35, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 35, 1, 
-	36, 36, 36, 36, 36, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 36, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 37, 
-	37, 37, 37, 37, 37, 37, 37, 37, 
-	37, 1, 1, 1, 38, 39, 1, 1, 
-	37, 37, 37, 37, 37, 37, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	37, 37, 37, 37, 37, 37, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 40, 1, 41, 41, 41, 
-	41, 41, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 41, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 42, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	43, 1, 1, 7, 7, 7, 7, 7, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 7, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 4, 
-	1, 44, 44, 44, 44, 44, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	44, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 45, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 46, 1, 44, 44, 
-	44, 44, 44, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 44, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 47, 47, 47, 
-	47, 47, 47, 47, 47, 47, 47, 1, 
-	1, 1, 1, 45, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 46, 1, 49, 49, 49, 49, 49, 
-	48, 48, 48, 48, 48, 48, 48, 48, 
-	48, 48, 48, 48, 48, 48, 48, 48, 
-	48, 48, 49, 48, 48, 50, 48, 48, 
-	48, 48, 48, 48, 48, 51, 1, 48, 
-	48, 48, 48, 48, 48, 48, 48, 48, 
-	48, 48, 48, 48, 48, 48, 48, 52, 
-	48, 48, 53, 48, 48, 48, 48, 48, 
-	48, 48, 48, 48, 48, 48, 48, 48, 
-	48, 48, 48, 48, 48, 48, 48, 48, 
-	48, 48, 48, 48, 48, 48, 54, 55, 
-	48, 48, 48, 48, 48, 48, 48, 48, 
-	48, 48, 48, 48, 48, 48, 48, 48, 
-	48, 48, 48, 48, 48, 48, 48, 48, 
-	48, 48, 48, 48, 48, 48, 56, 48, 
-	57, 57, 57, 57, 57, 35, 35, 35, 
-	35, 35, 35, 35, 35, 35, 35, 35, 
-	35, 35, 35, 35, 35, 35, 35, 57, 
-	35, 35, 58, 35, 35, 35, 35, 35, 
-	35, 35, 59, 1, 35, 35, 35, 35, 
-	35, 35, 35, 35, 35, 35, 35, 35, 
-	35, 35, 35, 35, 60, 35, 35, 61, 
-	35, 35, 35, 35, 35, 35, 35, 35, 
-	35, 35, 35, 35, 35, 35, 35, 35, 
-	35, 35, 35, 35, 35, 35, 35, 35, 
-	35, 35, 35, 62, 63, 35, 35, 35, 
-	35, 35, 35, 35, 35, 35, 35, 35, 
-	35, 35, 35, 35, 35, 35, 35, 35, 
-	35, 35, 35, 35, 35, 35, 35, 35, 
-	35, 35, 35, 64, 35, 65, 65, 65, 
-	65, 65, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 65, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 66, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	67, 1, 68, 68, 68, 68, 68, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 68, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 42, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 69, 1, 70, 
-	70, 70, 70, 70, 48, 48, 48, 48, 
-	48, 48, 48, 48, 48, 48, 48, 48, 
-	48, 48, 48, 48, 48, 48, 70, 48, 
-	48, 50, 48, 48, 48, 48, 48, 48, 
-	48, 51, 1, 48, 48, 48, 48, 48, 
-	48, 48, 48, 48, 48, 48, 48, 48, 
-	48, 48, 48, 52, 48, 48, 53, 48, 
-	48, 48, 48, 48, 48, 48, 48, 48, 
-	48, 48, 48, 48, 48, 48, 48, 48, 
-	48, 48, 48, 48, 48, 48, 48, 48, 
-	48, 48, 54, 55, 48, 48, 48, 48, 
-	48, 48, 48, 48, 48, 48, 48, 48, 
-	48, 48, 48, 48, 48, 48, 48, 48, 
-	48, 48, 48, 48, 48, 48, 48, 48, 
-	48, 48, 56, 48, 71, 71, 71, 71, 
-	71, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 71, 1, 1, 72, 1, 
-	1, 1, 1, 1, 1, 1, 1, 73, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	74, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 75, 
-	1, 76, 76, 76, 76, 76, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	76, 1, 1, 77, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 78, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 79, 1, 76, 76, 
-	76, 76, 76, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 76, 1, 1, 
-	77, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 21, 21, 21, 
-	21, 21, 21, 21, 21, 21, 21, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 78, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 79, 1, 71, 71, 71, 71, 71, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 71, 1, 1, 72, 1, 1, 
-	1, 1, 1, 1, 1, 1, 73, 1, 
-	1, 1, 16, 16, 16, 16, 16, 16, 
-	16, 16, 16, 16, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 74, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 75, 1, 
-	80, 80, 80, 80, 80, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 80, 
-	1, 1, 81, 1, 1, 1, 1, 1, 
-	1, 1, 82, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 83, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 45, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 84, 1, 85, 85, 85, 
-	85, 85, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 85, 1, 1, 86, 
-	1, 1, 1, 1, 1, 1, 1, 87, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 88, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	89, 1, 85, 85, 85, 85, 85, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 85, 1, 1, 86, 1, 1, 1, 
-	1, 1, 1, 1, 87, 1, 1, 1, 
-	1, 34, 34, 34, 34, 34, 34, 34, 
-	34, 34, 34, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 88, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 89, 1, 80, 
-	80, 80, 80, 80, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 80, 1, 
-	1, 81, 1, 1, 1, 1, 1, 1, 
-	1, 82, 1, 1, 1, 1, 90, 90, 
-	90, 90, 90, 90, 90, 90, 90, 90, 
-	1, 1, 1, 1, 1, 1, 83, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 45, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 84, 1, 65, 65, 65, 65, 
-	65, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 65, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 91, 91, 91, 91, 91, 
-	91, 91, 91, 91, 91, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	66, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 1, 1, 1, 1, 67, 
-	1, 0
-};
-
-static const char _deserialize_text_trans_targs[] = {
-	1, 0, 2, 26, 3, 4, 20, 5, 
-	24, 25, 28, 39, 9, 31, 34, 31, 
-	34, 11, 32, 33, 32, 33, 35, 38, 
-	14, 15, 18, 15, 18, 16, 17, 36, 
-	37, 36, 37, 27, 21, 20, 6, 22, 
-	23, 21, 22, 23, 21, 22, 23, 25, 
-	27, 27, 7, 8, 12, 13, 19, 22, 
-	30, 27, 7, 8, 12, 13, 19, 22, 
-	30, 29, 22, 30, 29, 30, 30, 29, 
-	7, 10, 22, 30, 29, 7, 22, 30, 
-	29, 7, 8, 13, 30, 29, 7, 8, 
-	22, 30, 38, 39
-};
-
-static const char _deserialize_text_trans_actions[] = {
-	0, 0, 0, 0, 1, 0, 2, 0, 
-	2, 2, 3, 3, 4, 3, 3, 5, 
-	5, 4, 3, 3, 5, 5, 3, 3, 
-	4, 4, 4, 0, 0, 6, 4, 3, 
-	3, 5, 5, 5, 7, 8, 9, 7, 
-	7, 0, 0, 0, 10, 10, 10, 8, 
-	12, 13, 14, 14, 14, 14, 15, 11, 
-	11, 17, 18, 18, 18, 18, 0, 16, 
-	16, 19, 19, 19, 0, 0, 13, 20, 
-	21, 21, 20, 20, 22, 23, 22, 22, 
-	10, 24, 24, 24, 10, 25, 26, 26, 
-	25, 25, 5, 5
-};
-
-static const char _deserialize_text_eof_actions[] = {
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 0, 7, 0, 0, 0, 
-	10, 10, 11, 16, 19, 0, 11, 20, 
-	22, 22, 20, 10, 25, 25, 10, 19
-};
-
-static const int deserialize_text_start = 1;
-static const int deserialize_text_first_final = 20;
-static const int deserialize_text_error = 0;
-
-static const int deserialize_text_en_main = 1;
-
-
-#line 117 "hb-buffer-deserialize-text.rl"
-
-
-static hb_bool_t
-_hb_buffer_deserialize_text (hb_buffer_t *buffer,
-				    const char *buf,
-				    unsigned int buf_len,
-				    const char **end_ptr,
-				    hb_font_t *font)
-{
-  const char *p = buf, *pe = buf + buf_len;
-
-  /* Ensure we have positions. */
-  (void) hb_buffer_get_glyph_positions (buffer, nullptr);
-
-  while (p < pe && ISSPACE (*p))
-    p++;
-
-  const char *eof = pe, *tok = nullptr;
-  int cs;
-  hb_glyph_info_t info = {0};
-  hb_glyph_position_t pos = {0};
-  
-#line 457 "hb-buffer-deserialize-text.hh"
-	{
-	cs = deserialize_text_start;
-	}
-
-#line 460 "hb-buffer-deserialize-text.hh"
-	{
-	int _slen;
-	int _trans;
-	const unsigned char *_keys;
-	const char *_inds;
-	if ( p == pe )
-		goto _test_eof;
-	if ( cs == 0 )
-		goto _out;
-_resume:
-	_keys = _deserialize_text_trans_keys + (cs<<1);
-	_inds = _deserialize_text_indicies + _deserialize_text_index_offsets[cs];
-
-	_slen = _deserialize_text_key_spans[cs];
-	_trans = _inds[ _slen > 0 && _keys[0] <=(*p) &&
-		(*p) <= _keys[1] ?
-		(*p) - _keys[0] : _slen ];
-
-	cs = _deserialize_text_trans_targs[_trans];
-
-	if ( _deserialize_text_trans_actions[_trans] == 0 )
-		goto _again;
-
-	switch ( _deserialize_text_trans_actions[_trans] ) {
-	case 1:
-#line 38 "hb-buffer-deserialize-text.rl"
-	{
-	hb_memset (&info, 0, sizeof (info));
-	hb_memset (&pos , 0, sizeof (pos ));
-}
-	break;
-	case 4:
-#line 51 "hb-buffer-deserialize-text.rl"
-	{
-	tok = p;
-}
-	break;
-	case 5:
-#line 55 "hb-buffer-deserialize-text.rl"
-	{ if (unlikely (!buffer->ensure_glyphs ())) return false; }
-	break;
-	case 8:
-#line 56 "hb-buffer-deserialize-text.rl"
-	{ if (unlikely (!buffer->ensure_unicode ())) return false; }
-	break;
-	case 18:
-#line 58 "hb-buffer-deserialize-text.rl"
-	{
-	/* TODO Unescape delimiters. */
-	if (!hb_font_glyph_from_string (font,
-					tok, p - tok,
-					&info.codepoint))
-	  return false;
-}
-	break;
-	case 9:
-#line 66 "hb-buffer-deserialize-text.rl"
-	{if (!parse_hex (tok, p, &info.codepoint )) return false; }
-	break;
-	case 24:
-#line 68 "hb-buffer-deserialize-text.rl"
-	{ if (!parse_uint (tok, p, &info.cluster )) return false; }
-	break;
-	case 6:
-#line 69 "hb-buffer-deserialize-text.rl"
-	{ if (!parse_int  (tok, p, &pos.x_offset )) return false; }
-	break;
-	case 26:
-#line 70 "hb-buffer-deserialize-text.rl"
-	{ if (!parse_int  (tok, p, &pos.y_offset )) return false; }
-	break;
-	case 21:
-#line 71 "hb-buffer-deserialize-text.rl"
-	{ if (!parse_int  (tok, p, &pos.x_advance)) return false; }
-	break;
-	case 23:
-#line 72 "hb-buffer-deserialize-text.rl"
-	{ if (!parse_int  (tok, p, &pos.y_advance)) return false; }
-	break;
-	case 15:
-#line 38 "hb-buffer-deserialize-text.rl"
-	{
-	hb_memset (&info, 0, sizeof (info));
-	hb_memset (&pos , 0, sizeof (pos ));
-}
-#line 51 "hb-buffer-deserialize-text.rl"
-	{
-	tok = p;
-}
-	break;
-	case 3:
-#line 51 "hb-buffer-deserialize-text.rl"
-	{
-	tok = p;
-}
-#line 55 "hb-buffer-deserialize-text.rl"
-	{ if (unlikely (!buffer->ensure_glyphs ())) return false; }
-	break;
-	case 2:
-#line 51 "hb-buffer-deserialize-text.rl"
-	{
-	tok = p;
-}
-#line 56 "hb-buffer-deserialize-text.rl"
-	{ if (unlikely (!buffer->ensure_unicode ())) return false; }
-	break;
-	case 16:
-#line 58 "hb-buffer-deserialize-text.rl"
-	{
-	/* TODO Unescape delimiters. */
-	if (!hb_font_glyph_from_string (font,
-					tok, p - tok,
-					&info.codepoint))
-	  return false;
-}
-#line 43 "hb-buffer-deserialize-text.rl"
-	{
-	buffer->add_info (info);
-	if (unlikely (!buffer->successful))
-	  return false;
-	buffer->pos[buffer->len - 1] = pos;
-	*end_ptr = p;
-}
-	break;
-	case 7:
-#line 66 "hb-buffer-deserialize-text.rl"
-	{if (!parse_hex (tok, p, &info.codepoint )) return false; }
-#line 43 "hb-buffer-deserialize-text.rl"
-	{
-	buffer->add_info (info);
-	if (unlikely (!buffer->successful))
-	  return false;
-	buffer->pos[buffer->len - 1] = pos;
-	*end_ptr = p;
-}
-	break;
-	case 10:
-#line 68 "hb-buffer-deserialize-text.rl"
-	{ if (!parse_uint (tok, p, &info.cluster )) return false; }
-#line 43 "hb-buffer-deserialize-text.rl"
-	{
-	buffer->add_info (info);
-	if (unlikely (!buffer->successful))
-	  return false;
-	buffer->pos[buffer->len - 1] = pos;
-	*end_ptr = p;
-}
-	break;
-	case 25:
-#line 70 "hb-buffer-deserialize-text.rl"
-	{ if (!parse_int  (tok, p, &pos.y_offset )) return false; }
-#line 43 "hb-buffer-deserialize-text.rl"
-	{
-	buffer->add_info (info);
-	if (unlikely (!buffer->successful))
-	  return false;
-	buffer->pos[buffer->len - 1] = pos;
-	*end_ptr = p;
-}
-	break;
-	case 20:
-#line 71 "hb-buffer-deserialize-text.rl"
-	{ if (!parse_int  (tok, p, &pos.x_advance)) return false; }
-#line 43 "hb-buffer-deserialize-text.rl"
-	{
-	buffer->add_info (info);
-	if (unlikely (!buffer->successful))
-	  return false;
-	buffer->pos[buffer->len - 1] = pos;
-	*end_ptr = p;
-}
-	break;
-	case 22:
-#line 72 "hb-buffer-deserialize-text.rl"
-	{ if (!parse_int  (tok, p, &pos.y_advance)) return false; }
-#line 43 "hb-buffer-deserialize-text.rl"
-	{
-	buffer->add_info (info);
-	if (unlikely (!buffer->successful))
-	  return false;
-	buffer->pos[buffer->len - 1] = pos;
-	*end_ptr = p;
-}
-	break;
-	case 19:
-#line 73 "hb-buffer-deserialize-text.rl"
-	{ if (!parse_uint (tok, p, &info.mask    )) return false; }
-#line 43 "hb-buffer-deserialize-text.rl"
-	{
-	buffer->add_info (info);
-	if (unlikely (!buffer->successful))
-	  return false;
-	buffer->pos[buffer->len - 1] = pos;
-	*end_ptr = p;
-}
-	break;
-	case 12:
-#line 38 "hb-buffer-deserialize-text.rl"
-	{
-	hb_memset (&info, 0, sizeof (info));
-	hb_memset (&pos , 0, sizeof (pos ));
-}
-#line 51 "hb-buffer-deserialize-text.rl"
-	{
-	tok = p;
-}
-#line 55 "hb-buffer-deserialize-text.rl"
-	{ if (unlikely (!buffer->ensure_glyphs ())) return false; }
-	break;
-	case 14:
-#line 38 "hb-buffer-deserialize-text.rl"
-	{
-	hb_memset (&info, 0, sizeof (info));
-	hb_memset (&pos , 0, sizeof (pos ));
-}
-#line 51 "hb-buffer-deserialize-text.rl"
-	{
-	tok = p;
-}
-#line 58 "hb-buffer-deserialize-text.rl"
-	{
-	/* TODO Unescape delimiters. */
-	if (!hb_font_glyph_from_string (font,
-					tok, p - tok,
-					&info.codepoint))
-	  return false;
-}
-	break;
-	case 17:
-#line 58 "hb-buffer-deserialize-text.rl"
-	{
-	/* TODO Unescape delimiters. */
-	if (!hb_font_glyph_from_string (font,
-					tok, p - tok,
-					&info.codepoint))
-	  return false;
-}
-#line 55 "hb-buffer-deserialize-text.rl"
-	{ if (unlikely (!buffer->ensure_glyphs ())) return false; }
-#line 43 "hb-buffer-deserialize-text.rl"
-	{
-	buffer->add_info (info);
-	if (unlikely (!buffer->successful))
-	  return false;
-	buffer->pos[buffer->len - 1] = pos;
-	*end_ptr = p;
-}
-	break;
-	case 11:
-#line 38 "hb-buffer-deserialize-text.rl"
-	{
-	hb_memset (&info, 0, sizeof (info));
-	hb_memset (&pos , 0, sizeof (pos ));
-}
-#line 51 "hb-buffer-deserialize-text.rl"
-	{
-	tok = p;
-}
-#line 58 "hb-buffer-deserialize-text.rl"
-	{
-	/* TODO Unescape delimiters. */
-	if (!hb_font_glyph_from_string (font,
-					tok, p - tok,
-					&info.codepoint))
-	  return false;
-}
-#line 43 "hb-buffer-deserialize-text.rl"
-	{
-	buffer->add_info (info);
-	if (unlikely (!buffer->successful))
-	  return false;
-	buffer->pos[buffer->len - 1] = pos;
-	*end_ptr = p;
-}
-	break;
-	case 13:
-#line 38 "hb-buffer-deserialize-text.rl"
-	{
-	hb_memset (&info, 0, sizeof (info));
-	hb_memset (&pos , 0, sizeof (pos ));
-}
-#line 51 "hb-buffer-deserialize-text.rl"
-	{
-	tok = p;
-}
-#line 58 "hb-buffer-deserialize-text.rl"
-	{
-	/* TODO Unescape delimiters. */
-	if (!hb_font_glyph_from_string (font,
-					tok, p - tok,
-					&info.codepoint))
-	  return false;
-}
-#line 55 "hb-buffer-deserialize-text.rl"
-	{ if (unlikely (!buffer->ensure_glyphs ())) return false; }
-#line 43 "hb-buffer-deserialize-text.rl"
-	{
-	buffer->add_info (info);
-	if (unlikely (!buffer->successful))
-	  return false;
-	buffer->pos[buffer->len - 1] = pos;
-	*end_ptr = p;
-}
-	break;
-#line 715 "hb-buffer-deserialize-text.hh"
-	}
-
-_again:
-	if ( cs == 0 )
-		goto _out;
-	if ( ++p != pe )
-		goto _resume;
-	_test_eof: {}
-	if ( p == eof )
-	{
-	switch ( _deserialize_text_eof_actions[cs] ) {
-	case 16:
-#line 58 "hb-buffer-deserialize-text.rl"
-	{
-	/* TODO Unescape delimiters. */
-	if (!hb_font_glyph_from_string (font,
-					tok, p - tok,
-					&info.codepoint))
-	  return false;
-}
-#line 43 "hb-buffer-deserialize-text.rl"
-	{
-	buffer->add_info (info);
-	if (unlikely (!buffer->successful))
-	  return false;
-	buffer->pos[buffer->len - 1] = pos;
-	*end_ptr = p;
-}
-	break;
-	case 7:
-#line 66 "hb-buffer-deserialize-text.rl"
-	{if (!parse_hex (tok, p, &info.codepoint )) return false; }
-#line 43 "hb-buffer-deserialize-text.rl"
-	{
-	buffer->add_info (info);
-	if (unlikely (!buffer->successful))
-	  return false;
-	buffer->pos[buffer->len - 1] = pos;
-	*end_ptr = p;
-}
-	break;
-	case 10:
-#line 68 "hb-buffer-deserialize-text.rl"
-	{ if (!parse_uint (tok, p, &info.cluster )) return false; }
-#line 43 "hb-buffer-deserialize-text.rl"
-	{
-	buffer->add_info (info);
-	if (unlikely (!buffer->successful))
-	  return false;
-	buffer->pos[buffer->len - 1] = pos;
-	*end_ptr = p;
-}
-	break;
-	case 25:
-#line 70 "hb-buffer-deserialize-text.rl"
-	{ if (!parse_int  (tok, p, &pos.y_offset )) return false; }
-#line 43 "hb-buffer-deserialize-text.rl"
-	{
-	buffer->add_info (info);
-	if (unlikely (!buffer->successful))
-	  return false;
-	buffer->pos[buffer->len - 1] = pos;
-	*end_ptr = p;
-}
-	break;
-	case 20:
-#line 71 "hb-buffer-deserialize-text.rl"
-	{ if (!parse_int  (tok, p, &pos.x_advance)) return false; }
-#line 43 "hb-buffer-deserialize-text.rl"
-	{
-	buffer->add_info (info);
-	if (unlikely (!buffer->successful))
-	  return false;
-	buffer->pos[buffer->len - 1] = pos;
-	*end_ptr = p;
-}
-	break;
-	case 22:
-#line 72 "hb-buffer-deserialize-text.rl"
-	{ if (!parse_int  (tok, p, &pos.y_advance)) return false; }
-#line 43 "hb-buffer-deserialize-text.rl"
-	{
-	buffer->add_info (info);
-	if (unlikely (!buffer->successful))
-	  return false;
-	buffer->pos[buffer->len - 1] = pos;
-	*end_ptr = p;
-}
-	break;
-	case 19:
-#line 73 "hb-buffer-deserialize-text.rl"
-	{ if (!parse_uint (tok, p, &info.mask    )) return false; }
-#line 43 "hb-buffer-deserialize-text.rl"
-	{
-	buffer->add_info (info);
-	if (unlikely (!buffer->successful))
-	  return false;
-	buffer->pos[buffer->len - 1] = pos;
-	*end_ptr = p;
-}
-	break;
-	case 11:
-#line 38 "hb-buffer-deserialize-text.rl"
-	{
-	hb_memset (&info, 0, sizeof (info));
-	hb_memset (&pos , 0, sizeof (pos ));
-}
-#line 51 "hb-buffer-deserialize-text.rl"
-	{
-	tok = p;
-}
-#line 58 "hb-buffer-deserialize-text.rl"
-	{
-	/* TODO Unescape delimiters. */
-	if (!hb_font_glyph_from_string (font,
-					tok, p - tok,
-					&info.codepoint))
-	  return false;
-}
-#line 43 "hb-buffer-deserialize-text.rl"
-	{
-	buffer->add_info (info);
-	if (unlikely (!buffer->successful))
-	  return false;
-	buffer->pos[buffer->len - 1] = pos;
-	*end_ptr = p;
-}
-	break;
-#line 825 "hb-buffer-deserialize-text.hh"
-	}
-	}
-
-	_out: {}
-	}
-
-#line 141 "hb-buffer-deserialize-text.rl"
-
-
-  *end_ptr = p;
-
-  return p == pe && *(p-1) != ']';
-}
-
-#endif /* HB_BUFFER_DESERIALIZE_TEXT_HH */

+ 12 - 9
thirdparty/harfbuzz/src/hb-buffer-serialize.cc

@@ -721,7 +721,8 @@ parse_hex (const char *pp, const char *end, uint32_t *pv)
 }
 
 #include "hb-buffer-deserialize-json.hh"
-#include "hb-buffer-deserialize-text.hh"
+#include "hb-buffer-deserialize-text-glyphs.hh"
+#include "hb-buffer-deserialize-text-unicode.hh"
 
 /**
  * hb_buffer_deserialize_glyphs:
@@ -736,7 +737,8 @@ parse_hex (const char *pp, const char *end, uint32_t *pv)
  * Deserializes glyphs @buffer from textual representation in the format
  * produced by hb_buffer_serialize_glyphs().
  *
- * Return value: `true` if @buf is not fully consumed, `false` otherwise.
+ * Return value: `true` if parse was successful, `false` if an error
+ * occurred.
  *
  * Since: 0.9.7
  **/
@@ -779,9 +781,9 @@ hb_buffer_deserialize_glyphs (hb_buffer_t *buffer,
   switch (format)
   {
     case HB_BUFFER_SERIALIZE_FORMAT_TEXT:
-      return _hb_buffer_deserialize_text (buffer,
-                                          buf, buf_len, end_ptr,
-                                          font);
+      return _hb_buffer_deserialize_text_glyphs (buffer,
+						 buf, buf_len, end_ptr,
+						 font);
 
     case HB_BUFFER_SERIALIZE_FORMAT_JSON:
       return _hb_buffer_deserialize_json (buffer,
@@ -808,7 +810,8 @@ hb_buffer_deserialize_glyphs (hb_buffer_t *buffer,
  * Deserializes Unicode @buffer from textual representation in the format
  * produced by hb_buffer_serialize_unicode().
  *
- * Return value: `true` if @buf is not fully consumed, `false` otherwise.
+ * Return value: `true` if parse was successful, `false` if an error
+ * occurred.
  *
  * Since: 2.7.3
  **/
@@ -849,9 +852,9 @@ hb_buffer_deserialize_unicode (hb_buffer_t *buffer,
   switch (format)
   {
     case HB_BUFFER_SERIALIZE_FORMAT_TEXT:
-      return _hb_buffer_deserialize_text (buffer,
-                                          buf, buf_len, end_ptr,
-                                          font);
+      return _hb_buffer_deserialize_text_unicode (buffer,
+						  buf, buf_len, end_ptr,
+						  font);
 
     case HB_BUFFER_SERIALIZE_FORMAT_JSON:
       return _hb_buffer_deserialize_json (buffer,

+ 2 - 2
thirdparty/harfbuzz/src/hb-buffer-verify.cc

@@ -150,7 +150,7 @@ buffer_verify_unsafe_to_break (hb_buffer_t  *buffer,
     assert (text_start < text_end);
 
     if (0)
-      printf("start %d end %d text start %d end %d\n", start, end, text_start, text_end);
+      printf("start %u end %u text start %u end %u\n", start, end, text_start, text_end);
 
     hb_buffer_clear_contents (fragment);
 
@@ -292,7 +292,7 @@ buffer_verify_unsafe_to_concat (hb_buffer_t        *buffer,
       assert (text_start < text_end);
 
       if (0)
-	printf("start %d end %d text start %d end %d\n", start, end, text_start, text_end);
+	printf("start %u end %u text start %u end %u\n", start, end, text_start, text_end);
 
 #if 0
       hb_buffer_flags_t flags = hb_buffer_get_flags (fragment);

+ 33 - 5
thirdparty/harfbuzz/src/hb-buffer.cc

@@ -522,15 +522,17 @@ hb_buffer_t::merge_clusters_impl (unsigned int start,
     cluster = hb_min (cluster, info[i].cluster);
 
   /* Extend end */
-  while (end < len && info[end - 1].cluster == info[end].cluster)
-    end++;
+  if (cluster != info[end - 1].cluster)
+    while (end < len && info[end - 1].cluster == info[end].cluster)
+      end++;
 
   /* Extend start */
-  while (idx < start && info[start - 1].cluster == info[start].cluster)
-    start--;
+  if (cluster != info[start].cluster)
+    while (idx < start && info[start - 1].cluster == info[start].cluster)
+      start--;
 
   /* If we hit the start of buffer, continue in out-buffer. */
-  if (idx == start)
+  if (idx == start && info[start].cluster != cluster)
     for (unsigned int i = out_len; i && out_info[i - 1].cluster == info[start].cluster; i--)
       set_cluster (out_info[i - 1], cluster);
 
@@ -893,6 +895,32 @@ hb_buffer_get_user_data (const hb_buffer_t  *buffer,
  * Sets the type of @buffer contents. Buffers are either empty, contain
  * characters (before shaping), or contain glyphs (the result of shaping).
  *
+ * You rarely need to call this function, since a number of other
+ * functions transition the content type for you. Namely:
+ *
+ * - A newly created buffer starts with content type
+ *   %HB_BUFFER_CONTENT_TYPE_INVALID. Calling hb_buffer_reset(),
+ *   hb_buffer_clear_contents(), as well as calling hb_buffer_set_length()
+ *   with an argument of zero all set the buffer content type to invalid
+ *   as well.
+ *
+ * - Calling hb_buffer_add_utf8(), hb_buffer_add_utf16(),
+ *   hb_buffer_add_utf32(), hb_buffer_add_codepoints() and
+ *   hb_buffer_add_latin1() expect that buffer is either empty and
+ *   have a content type of invalid, or that buffer content type is
+ *   %HB_BUFFER_CONTENT_TYPE_UNICODE, and they also set the content
+ *   type to Unicode if they added anything to an empty buffer.
+ *
+ * - Finally hb_shape() and hb_shape_full() expect that the buffer
+ *   is either empty and have content type of invalid, or that buffer
+ *   content type is %HB_BUFFER_CONTENT_TYPE_UNICODE, and upon
+ *   success they set the buffer content type to
+ *   %HB_BUFFER_CONTENT_TYPE_GLYPHS.
+ *
+ * The above transitions are designed such that one can use a buffer
+ * in a loop of "reset : add-text : shape" without needing to ever
+ * modify the content type manually.
+ *
  * Since: 0.9.5
  **/
 void

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

@@ -763,7 +763,7 @@ hb_buffer_diff (hb_buffer_t *buffer,
 
 
 /*
- * Debugging.
+ * Tracing.
  */
 
 /**

+ 44 - 26
thirdparty/harfbuzz/src/hb-buffer.hh

@@ -35,26 +35,6 @@
 #include "hb-set-digest.hh"
 
 
-#ifndef HB_BUFFER_MAX_LEN_FACTOR
-#define HB_BUFFER_MAX_LEN_FACTOR 64
-#endif
-#ifndef HB_BUFFER_MAX_LEN_MIN
-#define HB_BUFFER_MAX_LEN_MIN 16384
-#endif
-#ifndef HB_BUFFER_MAX_LEN_DEFAULT
-#define HB_BUFFER_MAX_LEN_DEFAULT 0x3FFFFFFF /* Shaping more than a billion chars? Let us know! */
-#endif
-
-#ifndef HB_BUFFER_MAX_OPS_FACTOR
-#define HB_BUFFER_MAX_OPS_FACTOR 1024
-#endif
-#ifndef HB_BUFFER_MAX_OPS_MIN
-#define HB_BUFFER_MAX_OPS_MIN 16384
-#endif
-#ifndef HB_BUFFER_MAX_OPS_DEFAULT
-#define HB_BUFFER_MAX_OPS_DEFAULT 0x1FFFFFFF /* Shaping more than a billion operations? Let us know! */
-#endif
-
 static_assert ((sizeof (hb_glyph_info_t) == 20), "");
 static_assert ((sizeof (hb_glyph_info_t) == sizeof (hb_glyph_position_t)), "");
 
@@ -601,21 +581,59 @@ struct hb_buffer_t
 			  unsigned int cluster,
 			  hb_mask_t mask)
   {
-    for (unsigned int i = start; i < end; i++)
-      if (cluster != infos[i].cluster)
+    if (unlikely (start == end))
+      return;
+
+    unsigned cluster_first = infos[start].cluster;
+    unsigned cluster_last = infos[end - 1].cluster;
+
+    if (cluster_level == HB_BUFFER_CLUSTER_LEVEL_CHARACTERS ||
+	(cluster != cluster_first && cluster != cluster_last))
+    {
+      for (unsigned int i = start; i < end; i++)
+	if (cluster != infos[i].cluster)
+	{
+	  scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS;
+	  infos[i].mask |= mask;
+	}
+      return;
+    }
+
+    /* Monotone clusters */
+
+    if (cluster == cluster_first)
+    {
+      for (unsigned int i = end; start < i && infos[i - 1].cluster != cluster_first; i--)
+      {
+	scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS;
+	infos[i - 1].mask |= mask;
+      }
+    }
+    else /* cluster == cluster_last */
+    {
+      for (unsigned int i = start; i < end && infos[i].cluster != cluster_last; i++)
       {
 	scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS;
 	infos[i].mask |= mask;
       }
+    }
   }
-  static unsigned
+  unsigned
   _infos_find_min_cluster (const hb_glyph_info_t *infos,
 			   unsigned start, unsigned end,
 			   unsigned cluster = UINT_MAX)
   {
-    for (unsigned int i = start; i < end; i++)
-      cluster = hb_min (cluster, infos[i].cluster);
-    return cluster;
+    if (unlikely (start == end))
+      return cluster;
+
+    if (cluster_level == HB_BUFFER_CLUSTER_LEVEL_CHARACTERS)
+    {
+      for (unsigned int i = start; i < end; i++)
+	cluster = hb_min (cluster, infos[i].cluster);
+      return cluster;
+    }
+
+    return hb_min (cluster, hb_min (infos[start].cluster, infos[end - 1].cluster));
   }
 
   void clear_glyph_flags (hb_mask_t mask = 0)

+ 5 - 2
thirdparty/harfbuzz/src/hb-cache.hh

@@ -39,7 +39,9 @@ template <unsigned int key_bits=16,
 struct hb_cache_t
 {
   using item_t = typename std::conditional<thread_safe,
-					   hb_atomic_int_t,
+					   typename std::conditional<key_bits + value_bits - cache_bits <= 16,
+								     hb_atomic_short_t,
+								     hb_atomic_int_t>::type,
 					   typename std::conditional<key_bits + value_bits - cache_bits <= 16,
 								     short,
 								     int>::type
@@ -48,8 +50,9 @@ struct hb_cache_t
   static_assert ((key_bits >= cache_bits), "");
   static_assert ((key_bits + value_bits <= cache_bits + 8 * sizeof (item_t)), "");
 
+  hb_cache_t () { init (); }
+
   void init () { clear (); }
-  void fini () {}
 
   void clear ()
   {

+ 869 - 0
thirdparty/harfbuzz/src/hb-cairo-utils.cc

@@ -0,0 +1,869 @@
+/*
+ * Copyright © 2022  Red Hat, 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): Matthias Clasen
+ */
+
+#include "hb.hh"
+
+#ifdef HAVE_CAIRO
+
+#include "hb-cairo-utils.hh"
+
+#include <cairo.h>
+
+#define PREALLOCATED_COLOR_STOPS 16
+
+#define _2_M_PIf (2.f * float (M_PI))
+
+typedef struct {
+  float r, g, b, a;
+} hb_cairo_color_t;
+
+static inline cairo_extend_t
+hb_cairo_extend (hb_paint_extend_t extend)
+{
+  switch (extend)
+    {
+    case HB_PAINT_EXTEND_PAD: return CAIRO_EXTEND_PAD;
+    case HB_PAINT_EXTEND_REPEAT: return CAIRO_EXTEND_REPEAT;
+    case HB_PAINT_EXTEND_REFLECT: return CAIRO_EXTEND_REFLECT;
+    default: break;
+    }
+
+  return CAIRO_EXTEND_PAD;
+}
+
+#ifdef CAIRO_HAS_PNG_FUNCTIONS
+typedef struct
+{
+  hb_blob_t *blob;
+  unsigned int offset;
+} hb_cairo_read_blob_data_t;
+
+static cairo_status_t
+hb_cairo_read_blob (void *closure,
+		    unsigned char *data,
+		    unsigned int length)
+{
+  hb_cairo_read_blob_data_t *r = (hb_cairo_read_blob_data_t *) closure;
+  const char *d;
+  unsigned int size;
+
+  d = hb_blob_get_data (r->blob, &size);
+
+  if (r->offset + length > size)
+    return CAIRO_STATUS_READ_ERROR;
+
+  memcpy (data, d + r->offset, length);
+  r->offset += length;
+
+  return CAIRO_STATUS_SUCCESS;
+}
+#endif
+
+static const cairo_user_data_key_t *_hb_cairo_surface_blob_user_data_key = {0};
+
+static void
+_hb_cairo_destroy_blob (void *p)
+{
+  hb_blob_destroy ((hb_blob_t *) p);
+}
+
+hb_bool_t
+_hb_cairo_paint_glyph_image (hb_cairo_context_t *c,
+			     hb_blob_t *blob,
+			     unsigned width,
+			     unsigned height,
+			     hb_tag_t format,
+			     float slant,
+			     hb_glyph_extents_t *extents)
+{
+  cairo_t *cr = c->cr;
+
+  if (!extents) /* SVG currently. */
+    return false;
+
+  cairo_surface_t *surface = nullptr;
+
+#ifdef CAIRO_HAS_PNG_FUNCTIONS
+  if (format == HB_PAINT_IMAGE_FORMAT_PNG)
+  {
+    hb_cairo_read_blob_data_t r;
+    r.blob = blob;
+    r.offset = 0;
+    surface = cairo_image_surface_create_from_png_stream (hb_cairo_read_blob, &r);
+
+    /* For PNG, width,height can be unreliable, as is the case for NotoColorEmoji :(.
+     * Just pull them out of the surface. */
+    width = cairo_image_surface_get_width (surface);
+    height = cairo_image_surface_get_width (surface);
+  }
+  else
+#endif
+  if (format == HB_PAINT_IMAGE_FORMAT_BGRA)
+  {
+    /* Byte-endian conversion. */
+    unsigned data_size = hb_blob_get_length (blob);
+    if (data_size < width * height * 4)
+      return false;
+
+    unsigned char *data;
+#ifdef __BYTE_ORDER
+    if (__BYTE_ORDER == __BIG_ENDIAN)
+    {
+      data = (unsigned char *) hb_blob_get_data_writable (blob, nullptr);
+      if (!data)
+        return false;
+
+      unsigned count = width * height * 4;
+      for (unsigned i = 0; i < count; i += 4)
+      {
+        unsigned char b;
+	b = data[i];
+	data[i] = data[i+3];
+	data[i+3] = b;
+	b = data[i+1];
+	data[i+1] = data[i+2];
+	data[i+2] = b;
+      }
+    }
+    else
+#endif
+      data = (unsigned char *) hb_blob_get_data (blob, nullptr);
+
+    surface = cairo_image_surface_create_for_data (data,
+						   CAIRO_FORMAT_ARGB32,
+						   width, height,
+						   width * 4);
+
+    cairo_surface_set_user_data (surface,
+				 _hb_cairo_surface_blob_user_data_key,
+				 hb_blob_reference (blob),
+				 _hb_cairo_destroy_blob);
+  }
+
+  if (!surface)
+    return false;
+
+  cairo_save (cr);
+  /* this clip is here to work around recording surface limitations */
+  cairo_rectangle (cr,
+                   extents->x_bearing,
+                   extents->y_bearing,
+                   extents->width,
+                   extents->height);
+  cairo_clip (cr);
+
+  cairo_pattern_t *pattern = cairo_pattern_create_for_surface (surface);
+  cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD);
+
+  cairo_matrix_t matrix = {(double) width, 0, 0, (double) height, 0, 0};
+  cairo_pattern_set_matrix (pattern, &matrix);
+
+  /* Undo slant in the extents and apply it in the context. */
+  extents->width -= extents->height * slant;
+  extents->x_bearing -= extents->y_bearing * slant;
+  cairo_matrix_t cairo_matrix = {1., 0., (double) slant, 1., 0., 0.};
+  cairo_transform (cr, &cairo_matrix);
+
+  cairo_translate (cr, extents->x_bearing, extents->y_bearing);
+  cairo_scale (cr, extents->width, extents->height);
+  cairo_set_source (cr, pattern);
+
+  cairo_paint (cr);
+
+  cairo_pattern_destroy (pattern);
+  cairo_surface_destroy (surface);
+
+  cairo_restore (cr);
+
+  return true;
+}
+
+static void
+_hb_cairo_reduce_anchors (float x0, float y0,
+			  float x1, float y1,
+			  float x2, float y2,
+			  float *xx0, float *yy0,
+			  float *xx1, float *yy1)
+{
+  float q1x, q1y, q2x, q2y;
+  float s;
+  float k;
+
+  q2x = x2 - x0;
+  q2y = y2 - y0;
+  q1x = x1 - x0;
+  q1y = y1 - y0;
+
+  s = q2x * q2x + q2y * q2y;
+  if (s < 0.000001f)
+    {
+      *xx0 = x0; *yy0 = y0;
+      *xx1 = x1; *yy1 = y1;
+      return;
+    }
+
+  k = (q2x * q1x + q2y * q1y) / s;
+  *xx0 = x0;
+  *yy0 = y0;
+  *xx1 = x1 - k * q2x;
+  *yy1 = y1 - k * q2y;
+}
+
+static int
+_hb_cairo_cmp_color_stop (const void *p1,
+			  const void *p2)
+{
+  const hb_color_stop_t *c1 = (const hb_color_stop_t *) p1;
+  const hb_color_stop_t *c2 = (const hb_color_stop_t *) p2;
+
+  if (c1->offset < c2->offset)
+    return -1;
+  else if (c1->offset > c2->offset)
+    return 1;
+  else
+    return 0;
+}
+
+static void
+_hb_cairo_normalize_color_line (hb_color_stop_t *stops,
+				unsigned int len,
+				float *omin,
+				float *omax)
+{
+  float min, max;
+
+  hb_qsort (stops, len, sizeof (hb_color_stop_t), _hb_cairo_cmp_color_stop);
+
+  min = max = stops[0].offset;
+  for (unsigned int i = 0; i < len; i++)
+    {
+      min = hb_min (min, stops[i].offset);
+      max = hb_max (max, stops[i].offset);
+    }
+
+  if (min != max)
+    {
+      for (unsigned int i = 0; i < len; i++)
+        stops[i].offset = (stops[i].offset - min) / (max - min);
+    }
+
+  *omin = min;
+  *omax = max;
+}
+
+static bool
+_hb_cairo_get_color_stops (hb_cairo_context_t *c,
+			   hb_color_line_t *color_line,
+			   unsigned *count,
+			   hb_color_stop_t **stops)
+{
+  unsigned len = hb_color_line_get_color_stops (color_line, 0, nullptr, nullptr);
+  if (len > *count)
+  {
+    *stops = (hb_color_stop_t *) hb_malloc (len * sizeof (hb_color_stop_t));
+    if (unlikely (!stops))
+      return false;
+  }
+  hb_color_line_get_color_stops (color_line, 0, &len, *stops);
+  for (unsigned i = 0; i < len; i++)
+    if ((*stops)[i].is_foreground)
+    {
+#ifdef HAVE_CAIRO_USER_SCALED_FONT_GET_FOREGROUND_SOURCE
+      double r, g, b, a;
+      cairo_pattern_t *foreground = cairo_user_scaled_font_get_foreground_source (c->scaled_font);
+      if (cairo_pattern_get_rgba (foreground, &r, &g, &b, &a) == CAIRO_STATUS_SUCCESS)
+        (*stops)[i].color = HB_COLOR (round (b * 255.), round (g * 255.), round (r * 255.),
+                                      round (a * hb_color_get_alpha ((*stops)[i].color)));
+      else
+#endif
+        (*stops)[i].color = HB_COLOR (0, 0, 0, hb_color_get_alpha ((*stops)[i].color));
+    }
+
+  *count = len;
+  return true;
+}
+
+void
+_hb_cairo_paint_linear_gradient (hb_cairo_context_t *c,
+				 hb_color_line_t *color_line,
+				 float x0, float y0,
+				 float x1, float y1,
+				 float x2, float y2)
+{
+  cairo_t *cr = c->cr;
+
+  unsigned int len = PREALLOCATED_COLOR_STOPS;
+  hb_color_stop_t stops_[PREALLOCATED_COLOR_STOPS];
+  hb_color_stop_t *stops = stops_;
+  float xx0, yy0, xx1, yy1;
+  float xxx0, yyy0, xxx1, yyy1;
+  float min, max;
+  cairo_pattern_t *pattern;
+
+  if (unlikely (!_hb_cairo_get_color_stops (c, color_line, &len, &stops)))
+    return;
+  _hb_cairo_normalize_color_line (stops, len, &min, &max);
+
+  _hb_cairo_reduce_anchors (x0, y0, x1, y1, x2, y2, &xx0, &yy0, &xx1, &yy1);
+
+  xxx0 = xx0 + min * (xx1 - xx0);
+  yyy0 = yy0 + min * (yy1 - yy0);
+  xxx1 = xx0 + max * (xx1 - xx0);
+  yyy1 = yy0 + max * (yy1 - yy0);
+
+  pattern = cairo_pattern_create_linear ((double) xxx0, (double) yyy0, (double) xxx1, (double) yyy1);
+  cairo_pattern_set_extend (pattern, hb_cairo_extend (hb_color_line_get_extend (color_line)));
+  for (unsigned int i = 0; i < len; i++)
+    {
+      double r, g, b, a;
+      r = hb_color_get_red (stops[i].color) / 255.;
+      g = hb_color_get_green (stops[i].color) / 255.;
+      b = hb_color_get_blue (stops[i].color) / 255.;
+      a = hb_color_get_alpha (stops[i].color) / 255.;
+      cairo_pattern_add_color_stop_rgba (pattern, (double) stops[i].offset, r, g, b, a);
+    }
+
+  cairo_set_source (cr, pattern);
+  cairo_paint (cr);
+
+  cairo_pattern_destroy (pattern);
+
+  if (stops != stops_)
+    hb_free (stops);
+}
+
+void
+_hb_cairo_paint_radial_gradient (hb_cairo_context_t *c,
+				 hb_color_line_t *color_line,
+				 float x0, float y0, float r0,
+				 float x1, float y1, float r1)
+{
+  cairo_t *cr = c->cr;
+
+  unsigned int len = PREALLOCATED_COLOR_STOPS;
+  hb_color_stop_t stops_[PREALLOCATED_COLOR_STOPS];
+  hb_color_stop_t *stops = stops_;
+  float min, max;
+  float xx0, yy0, xx1, yy1;
+  float rr0, rr1;
+  cairo_pattern_t *pattern;
+
+  if (unlikely (!_hb_cairo_get_color_stops (c, color_line, &len, &stops)))
+    return;
+  _hb_cairo_normalize_color_line (stops, len, &min, &max);
+
+  xx0 = x0 + min * (x1 - x0);
+  yy0 = y0 + min * (y1 - y0);
+  xx1 = x0 + max * (x1 - x0);
+  yy1 = y0 + max * (y1 - y0);
+  rr0 = r0 + min * (r1 - r0);
+  rr1 = r0 + max * (r1 - r0);
+
+  pattern = cairo_pattern_create_radial ((double) xx0, (double) yy0, (double) rr0, (double) xx1, (double) yy1, (double) rr1);
+  cairo_pattern_set_extend (pattern, hb_cairo_extend (hb_color_line_get_extend (color_line)));
+
+  for (unsigned int i = 0; i < len; i++)
+    {
+      double r, g, b, a;
+      r = hb_color_get_red (stops[i].color) / 255.;
+      g = hb_color_get_green (stops[i].color) / 255.;
+      b = hb_color_get_blue (stops[i].color) / 255.;
+      a = hb_color_get_alpha (stops[i].color) / 255.;
+      cairo_pattern_add_color_stop_rgba (pattern, (double) stops[i].offset, r, g, b, a);
+    }
+
+  cairo_set_source (cr, pattern);
+  cairo_paint (cr);
+
+  cairo_pattern_destroy (pattern);
+
+  if (stops != stops_)
+    hb_free (stops);
+}
+
+typedef struct {
+  float x, y;
+} hb_cairo_point_t;
+
+static inline float
+_hb_cairo_interpolate (float f0, float f1, float f)
+{
+  return f0 + f * (f1 - f0);
+}
+
+static inline void
+_hb_cairo_premultiply (hb_cairo_color_t *c)
+{
+  c->r *= c->a;
+  c->g *= c->a;
+  c->b *= c->a;
+}
+
+static inline void
+_hb_cairo_unpremultiply (hb_cairo_color_t *c)
+{
+  if (c->a != 0.f)
+  {
+     c->r /= c->a;
+     c->g /= c->a;
+     c->b /= c->a;
+  }
+}
+
+static void
+_hb_cairo_interpolate_colors (hb_cairo_color_t *c0, hb_cairo_color_t *c1, float k, hb_cairo_color_t *c)
+{
+  // According to the COLR specification, gradients
+  // should be interpolated in premultiplied form
+  _hb_cairo_premultiply (c0);
+  _hb_cairo_premultiply (c1);
+  c->r = c0->r + k * (c1->r - c0->r);
+  c->g = c0->g + k * (c1->g - c0->g);
+  c->b = c0->b + k * (c1->b - c0->b);
+  c->a = c0->a + k * (c1->a - c0->a);
+  _hb_cairo_unpremultiply (c);
+}
+
+static inline float
+_hb_cairo_dot (hb_cairo_point_t p, hb_cairo_point_t q)
+{
+  return p.x * q.x + p.y * q.y;
+}
+
+static inline hb_cairo_point_t
+_hb_cairo_normalize (hb_cairo_point_t p)
+{
+  float len = sqrtf (_hb_cairo_dot (p, p));
+
+  return hb_cairo_point_t { p.x / len, p.y / len };
+}
+
+static inline hb_cairo_point_t
+_hb_cairo_sum (hb_cairo_point_t p, hb_cairo_point_t q)
+{
+  return hb_cairo_point_t { p.x + q.x, p.y + q.y };
+}
+
+static inline hb_cairo_point_t
+_hb_cairo_difference (hb_cairo_point_t p, hb_cairo_point_t q)
+{
+  return hb_cairo_point_t { p.x - q.x, p.y - q.y };
+}
+
+static inline hb_cairo_point_t
+_hb_cairo_scale (hb_cairo_point_t p, float f)
+{
+  return hb_cairo_point_t { p.x * f, p.y * f };
+}
+
+typedef struct {
+  hb_cairo_point_t center, p0, c0, c1, p1;
+  hb_cairo_color_t color0, color1;
+} hb_cairo_patch_t;
+
+static void
+_hb_cairo_add_patch (cairo_pattern_t *pattern, hb_cairo_point_t *center, hb_cairo_patch_t *p)
+{
+  cairo_mesh_pattern_begin_patch (pattern);
+  cairo_mesh_pattern_move_to (pattern, (double) center->x, (double) center->y);
+  cairo_mesh_pattern_line_to (pattern, (double) p->p0.x, (double) p->p0.y);
+  cairo_mesh_pattern_curve_to (pattern,
+                               (double) p->c0.x, (double) p->c0.y,
+                               (double) p->c1.x, (double) p->c1.y,
+                               (double) p->p1.x, (double) p->p1.y);
+  cairo_mesh_pattern_line_to (pattern, (double) center->x, (double) center->y);
+  cairo_mesh_pattern_set_corner_color_rgba (pattern, 0,
+                                            (double) p->color0.r,
+                                            (double) p->color0.g,
+                                            (double) p->color0.b,
+                                            (double) p->color0.a);
+  cairo_mesh_pattern_set_corner_color_rgba (pattern, 1,
+                                            (double) p->color0.r,
+                                            (double) p->color0.g,
+                                            (double) p->color0.b,
+                                            (double) p->color0.a);
+  cairo_mesh_pattern_set_corner_color_rgba (pattern, 2,
+                                            (double) p->color1.r,
+                                            (double) p->color1.g,
+                                            (double) p->color1.b,
+                                            (double) p->color1.a);
+  cairo_mesh_pattern_set_corner_color_rgba (pattern, 3,
+                                            (double) p->color1.r,
+                                            (double) p->color1.g,
+                                            (double) p->color1.b,
+                                            (double) p->color1.a);
+  cairo_mesh_pattern_end_patch (pattern);
+}
+
+#define MAX_ANGLE ((float) M_PI / 8.f)
+
+static void
+_hb_cairo_add_sweep_gradient_patches1 (float cx, float cy, float radius,
+				       float a0, hb_cairo_color_t *c0,
+				       float a1, hb_cairo_color_t *c1,
+				       cairo_pattern_t *pattern)
+{
+  hb_cairo_point_t center = hb_cairo_point_t { cx, cy };
+  int num_splits;
+  hb_cairo_point_t p0;
+  hb_cairo_color_t color0, color1;
+
+  num_splits = ceilf (fabsf (a1 - a0) / MAX_ANGLE);
+  p0 = hb_cairo_point_t { cosf (a0), sinf (a0) };
+  color0 = *c0;
+
+  for (int a = 0; a < num_splits; a++)
+    {
+      float k = (a + 1.) / num_splits;
+      float angle1;
+      hb_cairo_point_t p1;
+      hb_cairo_point_t A, U;
+      hb_cairo_point_t C0, C1;
+      hb_cairo_patch_t patch;
+
+      angle1 = _hb_cairo_interpolate (a0, a1, k);
+      _hb_cairo_interpolate_colors (c0, c1, k, &color1);
+
+      patch.color0 = color0;
+      patch.color1 = color1;
+
+      p1 = hb_cairo_point_t { cosf (angle1), sinf (angle1) };
+      patch.p0 = _hb_cairo_sum (center, _hb_cairo_scale (p0, radius));
+      patch.p1 = _hb_cairo_sum (center, _hb_cairo_scale (p1, radius));
+
+      A = _hb_cairo_normalize (_hb_cairo_sum (p0, p1));
+      U = hb_cairo_point_t { -A.y, A.x };
+      C0 = _hb_cairo_sum (A, _hb_cairo_scale (U, _hb_cairo_dot (_hb_cairo_difference (p0, A), p0) / _hb_cairo_dot (U, p0)));
+      C1 = _hb_cairo_sum (A, _hb_cairo_scale (U, _hb_cairo_dot (_hb_cairo_difference (p1, A), p1) / _hb_cairo_dot (U, p1)));
+
+      patch.c0 = _hb_cairo_sum (center, _hb_cairo_scale (_hb_cairo_sum (C0, _hb_cairo_scale (_hb_cairo_difference (C0, p0), 0.33333f)), radius));
+      patch.c1 = _hb_cairo_sum (center, _hb_cairo_scale (_hb_cairo_sum (C1, _hb_cairo_scale (_hb_cairo_difference (C1, p1), 0.33333f)), radius));
+
+      _hb_cairo_add_patch (pattern, &center, &patch);
+
+      p0 = p1;
+      color0 = color1;
+    }
+}
+
+static void
+_hb_cairo_add_sweep_gradient_patches (hb_color_stop_t *stops,
+				      unsigned int n_stops,
+				      cairo_extend_t extend,
+				      float cx, float cy,
+				      float radius,
+				      float start_angle,
+				      float end_angle,
+				      cairo_pattern_t *pattern)
+{
+  float angles_[PREALLOCATED_COLOR_STOPS];
+  float *angles = angles_;
+  hb_cairo_color_t colors_[PREALLOCATED_COLOR_STOPS];
+  hb_cairo_color_t *colors = colors_;
+  hb_cairo_color_t color0, color1;
+
+  if (start_angle == end_angle)
+  {
+    if (extend == CAIRO_EXTEND_PAD)
+    {
+      hb_cairo_color_t c;
+      if (start_angle > 0)
+      {
+	c.r = hb_color_get_red (stops[0].color) / 255.;
+	c.g = hb_color_get_green (stops[0].color) / 255.;
+	c.b = hb_color_get_blue (stops[0].color) / 255.;
+	c.a = hb_color_get_alpha (stops[0].color) / 255.;
+	_hb_cairo_add_sweep_gradient_patches1 (cx, cy, radius,
+					       0.,          &c,
+					       start_angle, &c,
+					       pattern);
+      }
+      if (end_angle < _2_M_PIf)
+      {
+	c.r = hb_color_get_red (stops[n_stops - 1].color) / 255.;
+	c.g = hb_color_get_green (stops[n_stops - 1].color) / 255.;
+	c.b = hb_color_get_blue (stops[n_stops - 1].color) / 255.;
+	c.a = hb_color_get_alpha (stops[n_stops - 1].color) / 255.;
+	_hb_cairo_add_sweep_gradient_patches1 (cx, cy, radius,
+					       end_angle, &c,
+					       _2_M_PIf,  &c,
+					       pattern);
+      }
+    }
+    return;
+  }
+
+  assert (start_angle != end_angle);
+
+  /* handle directions */
+  if (end_angle < start_angle)
+  {
+    hb_swap (start_angle, end_angle);
+
+    for (unsigned i = 0; i < n_stops - 1 - i; i++)
+      hb_swap (stops[i], stops[n_stops - 1 - i]);
+    for (unsigned i = 0; i < n_stops; i++)
+      stops[i].offset = 1 - stops[i].offset;
+  }
+
+  if (n_stops > PREALLOCATED_COLOR_STOPS)
+  {
+    angles = (float *) hb_malloc (sizeof (float) * n_stops);
+    colors = (hb_cairo_color_t *) hb_malloc (sizeof (hb_cairo_color_t) * n_stops);
+    if (unlikely (!angles || !colors))
+    {
+      hb_free (angles);
+      hb_free (colors);
+      return;
+    }
+  }
+
+  for (unsigned i = 0; i < n_stops; i++)
+  {
+    angles[i] = start_angle + stops[i].offset * (end_angle - start_angle);
+    colors[i].r = hb_color_get_red (stops[i].color) / 255.;
+    colors[i].g = hb_color_get_green (stops[i].color) / 255.;
+    colors[i].b = hb_color_get_blue (stops[i].color) / 255.;
+    colors[i].a = hb_color_get_alpha (stops[i].color) / 255.;
+  }
+
+  if (extend == CAIRO_EXTEND_PAD)
+  {
+    unsigned pos;
+
+    color0 = colors[0];
+    for (pos = 0; pos < n_stops; pos++)
+    {
+      if (angles[pos] >= 0)
+      {
+	if (pos > 0)
+	{
+	  float k = (0 - angles[pos - 1]) / (angles[pos] - angles[pos - 1]);
+	  _hb_cairo_interpolate_colors (&colors[pos-1], &colors[pos], k, &color0);
+	}
+	break;
+      }
+    }
+    if (pos == n_stops)
+    {
+      /* everything is below 0 */
+      color0 = colors[n_stops-1];
+      _hb_cairo_add_sweep_gradient_patches1 (cx, cy, radius,
+					     0.,       &color0,
+					     _2_M_PIf, &color0,
+					     pattern);
+      goto done;
+    }
+
+    _hb_cairo_add_sweep_gradient_patches1 (cx, cy, radius,
+					   0.,          &color0,
+					   angles[pos], &colors[pos],
+					   pattern);
+
+    for (pos++; pos < n_stops; pos++)
+    {
+      if (angles[pos] <= _2_M_PIf)
+      {
+	_hb_cairo_add_sweep_gradient_patches1 (cx, cy, radius,
+					       angles[pos - 1], &colors[pos-1],
+					       angles[pos],     &colors[pos],
+					       pattern);
+      }
+      else
+      {
+	float k = (_2_M_PIf - angles[pos - 1]) / (angles[pos] - angles[pos - 1]);
+	_hb_cairo_interpolate_colors (&colors[pos - 1], &colors[pos], k, &color1);
+	_hb_cairo_add_sweep_gradient_patches1 (cx, cy, radius,
+					       angles[pos - 1], &colors[pos - 1],
+					       _2_M_PIf,        &color1,
+					       pattern);
+	break;
+      }
+    }
+
+    if (pos == n_stops)
+    {
+      /* everything is below 2*M_PI */
+      color0 = colors[n_stops - 1];
+      _hb_cairo_add_sweep_gradient_patches1 (cx, cy, radius,
+					     angles[n_stops - 1], &color0,
+					     _2_M_PIf,            &color0,
+					     pattern);
+      goto done;
+    }
+  }
+  else
+  {
+    int k;
+    float span;
+
+    span = angles[n_stops - 1] - angles[0];
+    k = 0;
+    if (angles[0] >= 0)
+    {
+      float ss = angles[0];
+      while (ss > 0)
+      {
+	if (span > 0)
+	{
+	  ss -= span;
+	  k--;
+	}
+	else
+	{
+	  ss += span;
+	  k++;
+	}
+      }
+    }
+    else if (angles[0] < 0)
+    {
+      float ee = angles[n_stops - 1];
+      while (ee < 0)
+      {
+	if (span > 0)
+	{
+	  ee += span;
+	  k++;
+	}
+	else
+	{
+	  ee -= span;
+	  k--;
+	}
+      }
+    }
+
+    //assert (angles[0] + k * span <= 0 && 0 < angles[n_stops - 1] + k * span);
+    span = fabs (span);
+
+    for (signed l = k; l < 1000; l++)
+    {
+      for (unsigned i = 1; i < n_stops; i++)
+      {
+        float a0, a1;
+	hb_cairo_color_t *c0, *c1;
+
+	if ((l % 2 != 0) && (extend == CAIRO_EXTEND_REFLECT))
+	{
+	  a0 = angles[0] + angles[n_stops - 1] - angles[n_stops - 1 - (i-1)] + l * span;
+	  a1 = angles[0] + angles[n_stops - 1] - angles[n_stops - 1 - i] + l * span;
+	  c0 = &colors[n_stops - 1 - (i - 1)];
+	  c1 = &colors[n_stops - 1 - i];
+	}
+	else
+	{
+	  a0 = angles[i-1] + l * span;
+	  a1 = angles[i] + l * span;
+	  c0 = &colors[i-1];
+	  c1 = &colors[i];
+	}
+
+	if (a1 < 0)
+	  continue;
+	if (a0 < 0)
+	{
+	  hb_cairo_color_t color;
+	  float f = (0 - a0)/(a1 - a0);
+	  _hb_cairo_interpolate_colors (c0, c1, f, &color);
+	  _hb_cairo_add_sweep_gradient_patches1 (cx, cy, radius,
+						 0,  &color,
+						 a1, c1,
+						 pattern);
+	}
+	else if (a1 >= _2_M_PIf)
+	{
+	  hb_cairo_color_t color;
+	  float f = (_2_M_PIf - a0)/(a1 - a0);
+	  _hb_cairo_interpolate_colors (c0, c1, f, &color);
+	  _hb_cairo_add_sweep_gradient_patches1 (cx, cy, radius,
+						 a0,       c0,
+						 _2_M_PIf, &color,
+						 pattern);
+	  goto done;
+	}
+	else
+	{
+	  _hb_cairo_add_sweep_gradient_patches1 (cx, cy, radius,
+						 a0, c0,
+						 a1, c1,
+						 pattern);
+	}
+      }
+    }
+  }
+
+done:
+
+  if (angles != angles_)
+    hb_free (angles);
+  if (colors != colors_)
+    hb_free (colors);
+}
+
+void
+_hb_cairo_paint_sweep_gradient (hb_cairo_context_t *c,
+				hb_color_line_t *color_line,
+				float cx, float cy,
+				float start_angle,
+				float end_angle)
+{
+  cairo_t *cr = c->cr;
+
+  unsigned int len = PREALLOCATED_COLOR_STOPS;
+  hb_color_stop_t stops_[PREALLOCATED_COLOR_STOPS];
+  hb_color_stop_t *stops = stops_;
+  cairo_extend_t extend;
+  double x1, y1, x2, y2;
+  float max_x, max_y, radius;
+  cairo_pattern_t *pattern;
+
+  if (unlikely (!_hb_cairo_get_color_stops (c, color_line, &len, &stops)))
+    return;
+
+  hb_qsort (stops, len, sizeof (hb_color_stop_t), _hb_cairo_cmp_color_stop);
+
+  cairo_clip_extents (cr, &x1, &y1, &x2, &y2);
+  max_x = (float) hb_max ((x1 - (double) cx) * (x1 - (double) cx), (x2 - (double) cx) * (x2 - (double) cx));
+  max_y = (float) hb_max ((y1 - (double) cy) * (y1 - (double) cy), (y2 - (double) cy) * (y2 - (double) cy));
+  radius = sqrtf (max_x + max_y);
+
+  extend = hb_cairo_extend (hb_color_line_get_extend (color_line));
+  pattern = cairo_pattern_create_mesh ();
+
+  _hb_cairo_add_sweep_gradient_patches (stops, len, extend, cx, cy,
+					radius, start_angle, end_angle, pattern);
+
+  cairo_set_source (cr, pattern);
+  cairo_paint (cr);
+
+  cairo_pattern_destroy (pattern);
+
+  if (stops != stops_)
+    hb_free (stops);
+}
+
+#endif

+ 107 - 0
thirdparty/harfbuzz/src/hb-cairo-utils.hh

@@ -0,0 +1,107 @@
+/*
+ * Copyright © 2022  Red Hat, 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): Matthias Clasen
+ */
+
+#ifndef HB_CAIRO_UTILS_H
+#define HB_CAIRO_UTILS_H
+
+#include "hb.hh"
+#include "hb-cairo.h"
+
+
+typedef struct
+{
+  cairo_scaled_font_t *scaled_font;
+  cairo_t *cr;
+  hb_map_t *color_cache;
+} hb_cairo_context_t;
+
+static inline cairo_operator_t
+_hb_paint_composite_mode_to_cairo (hb_paint_composite_mode_t mode)
+{
+  switch (mode)
+    {
+    case HB_PAINT_COMPOSITE_MODE_CLEAR: return CAIRO_OPERATOR_CLEAR;
+    case HB_PAINT_COMPOSITE_MODE_SRC: return CAIRO_OPERATOR_SOURCE;
+    case HB_PAINT_COMPOSITE_MODE_DEST: return CAIRO_OPERATOR_DEST;
+    case HB_PAINT_COMPOSITE_MODE_SRC_OVER: return CAIRO_OPERATOR_OVER;
+    case HB_PAINT_COMPOSITE_MODE_DEST_OVER: return CAIRO_OPERATOR_DEST_OVER;
+    case HB_PAINT_COMPOSITE_MODE_SRC_IN: return CAIRO_OPERATOR_IN;
+    case HB_PAINT_COMPOSITE_MODE_DEST_IN: return CAIRO_OPERATOR_DEST_IN;
+    case HB_PAINT_COMPOSITE_MODE_SRC_OUT: return CAIRO_OPERATOR_OUT;
+    case HB_PAINT_COMPOSITE_MODE_DEST_OUT: return CAIRO_OPERATOR_DEST_OUT;
+    case HB_PAINT_COMPOSITE_MODE_SRC_ATOP: return CAIRO_OPERATOR_ATOP;
+    case HB_PAINT_COMPOSITE_MODE_DEST_ATOP: return CAIRO_OPERATOR_DEST_ATOP;
+    case HB_PAINT_COMPOSITE_MODE_XOR: return CAIRO_OPERATOR_XOR;
+    case HB_PAINT_COMPOSITE_MODE_PLUS: return CAIRO_OPERATOR_ADD;
+    case HB_PAINT_COMPOSITE_MODE_SCREEN: return CAIRO_OPERATOR_SCREEN;
+    case HB_PAINT_COMPOSITE_MODE_OVERLAY: return CAIRO_OPERATOR_OVERLAY;
+    case HB_PAINT_COMPOSITE_MODE_DARKEN: return CAIRO_OPERATOR_DARKEN;
+    case HB_PAINT_COMPOSITE_MODE_LIGHTEN: return CAIRO_OPERATOR_LIGHTEN;
+    case HB_PAINT_COMPOSITE_MODE_COLOR_DODGE: return CAIRO_OPERATOR_COLOR_DODGE;
+    case HB_PAINT_COMPOSITE_MODE_COLOR_BURN: return CAIRO_OPERATOR_COLOR_BURN;
+    case HB_PAINT_COMPOSITE_MODE_HARD_LIGHT: return CAIRO_OPERATOR_HARD_LIGHT;
+    case HB_PAINT_COMPOSITE_MODE_SOFT_LIGHT: return CAIRO_OPERATOR_SOFT_LIGHT;
+    case HB_PAINT_COMPOSITE_MODE_DIFFERENCE: return CAIRO_OPERATOR_DIFFERENCE;
+    case HB_PAINT_COMPOSITE_MODE_EXCLUSION: return CAIRO_OPERATOR_EXCLUSION;
+    case HB_PAINT_COMPOSITE_MODE_MULTIPLY: return CAIRO_OPERATOR_MULTIPLY;
+    case HB_PAINT_COMPOSITE_MODE_HSL_HUE: return CAIRO_OPERATOR_HSL_HUE;
+    case HB_PAINT_COMPOSITE_MODE_HSL_SATURATION: return CAIRO_OPERATOR_HSL_SATURATION;
+    case HB_PAINT_COMPOSITE_MODE_HSL_COLOR: return CAIRO_OPERATOR_HSL_COLOR;
+    case HB_PAINT_COMPOSITE_MODE_HSL_LUMINOSITY: return CAIRO_OPERATOR_HSL_LUMINOSITY;
+    default: return CAIRO_OPERATOR_CLEAR;
+    }
+}
+
+HB_INTERNAL hb_bool_t
+_hb_cairo_paint_glyph_image (hb_cairo_context_t *c,
+			     hb_blob_t *blob,
+			     unsigned width,
+			     unsigned height,
+			     hb_tag_t format,
+			     float slant,
+			     hb_glyph_extents_t *extents);
+
+HB_INTERNAL void
+_hb_cairo_paint_linear_gradient (hb_cairo_context_t *c,
+				 hb_color_line_t *color_line,
+				 float x0, float y0,
+				 float x1, float y1,
+				 float x2, float y2);
+
+HB_INTERNAL void
+_hb_cairo_paint_radial_gradient (hb_cairo_context_t *c,
+				 hb_color_line_t *color_line,
+				 float x0, float y0, float r0,
+				 float x1, float y1, float r1);
+
+HB_INTERNAL void
+_hb_cairo_paint_sweep_gradient (hb_cairo_context_t *c,
+				hb_color_line_t *color_line,
+				float x0, float y0,
+				float start_angle, float end_angle);
+
+
+#endif /* HB_CAIRO_UTILS_H */

+ 1010 - 0
thirdparty/harfbuzz/src/hb-cairo.cc

@@ -0,0 +1,1010 @@
+/*
+ * Copyright © 2022  Red Hat, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Matthias Clasen
+ */
+
+#include "hb.hh"
+
+#ifdef HAVE_CAIRO
+
+#include "hb-cairo.h"
+
+#include "hb-cairo-utils.hh"
+
+#include "hb-machinery.hh"
+#include "hb-utf.hh"
+
+
+/**
+ * SECTION:hb-cairo
+ * @title: hb-cairo
+ * @short_description: Cairo integration
+ * @include: hb-cairo.h
+ *
+ * Functions for using HarfBuzz with the cairo library.
+ *
+ * HarfBuzz supports using cairo for rendering.
+ **/
+
+static void
+hb_cairo_move_to (hb_draw_funcs_t *dfuncs HB_UNUSED,
+		  void *draw_data,
+		  hb_draw_state_t *st HB_UNUSED,
+		  float to_x, float to_y,
+		  void *user_data HB_UNUSED)
+{
+  cairo_t *cr = (cairo_t *) draw_data;
+
+  cairo_move_to (cr, (double) to_x, (double) to_y);
+}
+
+static void
+hb_cairo_line_to (hb_draw_funcs_t *dfuncs HB_UNUSED,
+		  void *draw_data,
+		  hb_draw_state_t *st HB_UNUSED,
+		  float to_x, float to_y,
+		  void *user_data HB_UNUSED)
+{
+  cairo_t *cr = (cairo_t *) draw_data;
+
+  cairo_line_to (cr, (double) to_x, (double) to_y);
+}
+
+static void
+hb_cairo_cubic_to (hb_draw_funcs_t *dfuncs HB_UNUSED,
+		   void *draw_data,
+		   hb_draw_state_t *st HB_UNUSED,
+		   float control1_x, float control1_y,
+		   float control2_x, float control2_y,
+		   float to_x, float to_y,
+		   void *user_data HB_UNUSED)
+{
+  cairo_t *cr = (cairo_t *) draw_data;
+
+  cairo_curve_to (cr,
+                  (double) control1_x, (double) control1_y,
+                  (double) control2_x, (double) control2_y,
+                  (double) to_x, (double) to_y);
+}
+
+static void
+hb_cairo_close_path (hb_draw_funcs_t *dfuncs HB_UNUSED,
+		     void *draw_data,
+		     hb_draw_state_t *st HB_UNUSED,
+		     void *user_data HB_UNUSED)
+{
+  cairo_t *cr = (cairo_t *) draw_data;
+
+  cairo_close_path (cr);
+}
+
+static inline void free_static_cairo_draw_funcs ();
+
+static struct hb_cairo_draw_funcs_lazy_loader_t : hb_draw_funcs_lazy_loader_t<hb_cairo_draw_funcs_lazy_loader_t>
+{
+  static hb_draw_funcs_t *create ()
+  {
+    hb_draw_funcs_t *funcs = hb_draw_funcs_create ();
+
+    hb_draw_funcs_set_move_to_func (funcs, hb_cairo_move_to, nullptr, nullptr);
+    hb_draw_funcs_set_line_to_func (funcs, hb_cairo_line_to, nullptr, nullptr);
+    hb_draw_funcs_set_cubic_to_func (funcs, hb_cairo_cubic_to, nullptr, nullptr);
+    hb_draw_funcs_set_close_path_func (funcs, hb_cairo_close_path, nullptr, nullptr);
+
+    hb_draw_funcs_make_immutable (funcs);
+
+    hb_atexit (free_static_cairo_draw_funcs);
+
+    return funcs;
+  }
+} static_cairo_draw_funcs;
+
+static inline
+void free_static_cairo_draw_funcs ()
+{
+  static_cairo_draw_funcs.free_instance ();
+}
+
+static hb_draw_funcs_t *
+hb_cairo_draw_get_funcs ()
+{
+  return static_cairo_draw_funcs.get_unconst ();
+}
+
+
+#ifdef HAVE_CAIRO_USER_FONT_FACE_SET_RENDER_COLOR_GLYPH_FUNC
+
+static void
+hb_cairo_push_transform (hb_paint_funcs_t *pfuncs HB_UNUSED,
+			 void *paint_data,
+			 float xx, float yx,
+			 float xy, float yy,
+			 float dx, float dy,
+			 void *user_data HB_UNUSED)
+{
+  hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data;
+  cairo_t *cr = c->cr;
+
+  cairo_matrix_t m;
+
+  cairo_save (cr);
+  cairo_matrix_init (&m, (double) xx, (double) yx,
+                         (double) xy, (double) yy,
+                         (double) dx, (double) dy);
+  cairo_transform (cr, &m);
+}
+
+static void
+hb_cairo_pop_transform (hb_paint_funcs_t *pfuncs HB_UNUSED,
+		        void *paint_data,
+		        void *user_data HB_UNUSED)
+{
+  hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data;
+  cairo_t *cr = c->cr;
+
+  cairo_restore (cr);
+}
+
+static void
+hb_cairo_push_clip_glyph (hb_paint_funcs_t *pfuncs HB_UNUSED,
+			  void *paint_data,
+			  hb_codepoint_t glyph,
+			  hb_font_t *font,
+			  void *user_data HB_UNUSED)
+{
+  hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data;
+  cairo_t *cr = c->cr;
+
+  cairo_save (cr);
+  cairo_new_path (cr);
+  hb_font_draw_glyph (font, glyph, hb_cairo_draw_get_funcs (), cr);
+  cairo_close_path (cr);
+  cairo_clip (cr);
+}
+
+static void
+hb_cairo_push_clip_rectangle (hb_paint_funcs_t *pfuncs HB_UNUSED,
+			      void *paint_data,
+			      float xmin, float ymin, float xmax, float ymax,
+			      void *user_data HB_UNUSED)
+{
+  hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data;
+  cairo_t *cr = c->cr;
+
+  cairo_save (cr);
+  cairo_rectangle (cr,
+                   (double) xmin, (double) ymin,
+                   (double) (xmax - xmin), (double) (ymax - ymin));
+  cairo_clip (cr);
+}
+
+static void
+hb_cairo_pop_clip (hb_paint_funcs_t *pfuncs HB_UNUSED,
+		   void *paint_data,
+		   void *user_data HB_UNUSED)
+{
+  hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data;
+  cairo_t *cr = c->cr;
+
+  cairo_restore (cr);
+}
+
+static void
+hb_cairo_push_group (hb_paint_funcs_t *pfuncs HB_UNUSED,
+		     void *paint_data,
+		     void *user_data HB_UNUSED)
+{
+  hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data;
+  cairo_t *cr = c->cr;
+
+  cairo_save (cr);
+  cairo_push_group (cr);
+}
+
+static void
+hb_cairo_pop_group (hb_paint_funcs_t *pfuncs HB_UNUSED,
+		    void *paint_data,
+		    hb_paint_composite_mode_t mode,
+		    void *user_data HB_UNUSED)
+{
+  hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data;
+  cairo_t *cr = c->cr;
+
+  cairo_pop_group_to_source (cr);
+  cairo_set_operator (cr, _hb_paint_composite_mode_to_cairo (mode));
+  cairo_paint (cr);
+
+  cairo_restore (cr);
+}
+
+static void
+hb_cairo_paint_color (hb_paint_funcs_t *pfuncs HB_UNUSED,
+		      void *paint_data,
+		      hb_bool_t use_foreground,
+		      hb_color_t color,
+		      void *user_data HB_UNUSED)
+{
+  hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data;
+  cairo_t *cr = c->cr;
+
+  if (use_foreground)
+  {
+#ifdef HAVE_CAIRO_USER_SCALED_FONT_GET_FOREGROUND_SOURCE
+    double r, g, b, a;
+    cairo_pattern_t *foreground = cairo_user_scaled_font_get_foreground_source (c->scaled_font);
+    if (cairo_pattern_get_rgba (foreground, &r, &g, &b, &a) == CAIRO_STATUS_SUCCESS)
+      cairo_set_source_rgba (cr, r, g, b, a * hb_color_get_alpha (color) / 255.);
+    else
+#endif
+      cairo_set_source_rgba (cr, 0, 0, 0, hb_color_get_alpha (color) / 255.);
+  }
+  else
+    cairo_set_source_rgba (cr,
+			   hb_color_get_red (color) / 255.,
+			   hb_color_get_green (color) / 255.,
+			   hb_color_get_blue (color) / 255.,
+			   hb_color_get_alpha (color) / 255.);
+  cairo_paint (cr);
+}
+
+static hb_bool_t
+hb_cairo_paint_image (hb_paint_funcs_t *pfuncs HB_UNUSED,
+		      void *paint_data,
+		      hb_blob_t *blob,
+		      unsigned width,
+		      unsigned height,
+		      hb_tag_t format,
+		      float slant,
+		      hb_glyph_extents_t *extents,
+		      void *user_data HB_UNUSED)
+{
+  hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data;
+
+  return _hb_cairo_paint_glyph_image (c, blob, width, height, format, slant, extents);
+}
+
+static void
+hb_cairo_paint_linear_gradient (hb_paint_funcs_t *pfuncs HB_UNUSED,
+				void *paint_data,
+				hb_color_line_t *color_line,
+				float x0, float y0,
+				float x1, float y1,
+				float x2, float y2,
+				void *user_data HB_UNUSED)
+{
+  hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data;
+
+  _hb_cairo_paint_linear_gradient (c, color_line, x0, y0, x1, y1, x2, y2);
+}
+
+static void
+hb_cairo_paint_radial_gradient (hb_paint_funcs_t *pfuncs HB_UNUSED,
+				void *paint_data,
+				hb_color_line_t *color_line,
+				float x0, float y0, float r0,
+				float x1, float y1, float r1,
+				void *user_data HB_UNUSED)
+{
+  hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data;
+
+  _hb_cairo_paint_radial_gradient (c, color_line, x0, y0, r0, x1, y1, r1);
+}
+
+static void
+hb_cairo_paint_sweep_gradient (hb_paint_funcs_t *pfuncs HB_UNUSED,
+			       void *paint_data,
+			       hb_color_line_t *color_line,
+			       float x0, float y0,
+			       float start_angle, float end_angle,
+			       void *user_data HB_UNUSED)
+{
+  hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data;
+
+  _hb_cairo_paint_sweep_gradient (c, color_line, x0, y0, start_angle, end_angle);
+}
+
+static const cairo_user_data_key_t color_cache_key = {0};
+
+static void
+_hb_cairo_destroy_map (void *p)
+{
+  hb_map_destroy ((hb_map_t *) p);
+}
+
+static hb_bool_t
+hb_cairo_paint_custom_palette_color (hb_paint_funcs_t *funcs,
+                                     void *paint_data,
+                                     unsigned int color_index,
+                                     hb_color_t *color,
+                                     void *user_data HB_UNUSED)
+{
+#ifdef HAVE_CAIRO_FONT_OPTIONS_GET_CUSTOM_PALETTE_COLOR
+  hb_cairo_context_t *c = (hb_cairo_context_t *) paint_data;
+  cairo_t *cr = c->cr;
+
+#define HB_DEADBEEF HB_TAG(0xDE,0xAD,0xBE,0xEF)
+
+  hb_map_t *color_cache = c->color_cache;
+  hb_codepoint_t *v;
+  if (likely (color_cache && color_cache->has (color_index, &v)))
+  {
+    if (*v == HB_DEADBEEF)
+      return false;
+    *color = *v;
+    return true;
+  }
+
+  cairo_font_options_t *options;
+  double red, green, blue, alpha;
+
+  options = cairo_font_options_create ();
+  cairo_get_font_options (cr, options);
+  if (CAIRO_STATUS_SUCCESS ==
+      cairo_font_options_get_custom_palette_color (options, color_index,
+                                                   &red, &green, &blue, &alpha))
+  {
+    cairo_font_options_destroy (options);
+    *color = HB_COLOR (round (255 * blue),
+		       round (255 * green),
+		       round (255 * red),
+		       round (255 * alpha));
+
+    if (likely (color_cache && *color != HB_DEADBEEF))
+      color_cache->set (color_index, *color);
+
+    return true;
+  }
+  cairo_font_options_destroy (options);
+
+  if (likely (color_cache))
+    color_cache->set (color_index, HB_DEADBEEF);
+
+#undef HB_DEADBEEF
+
+#endif
+
+  return false;
+}
+
+static inline void free_static_cairo_paint_funcs ();
+
+static struct hb_cairo_paint_funcs_lazy_loader_t : hb_paint_funcs_lazy_loader_t<hb_cairo_paint_funcs_lazy_loader_t>
+{
+  static hb_paint_funcs_t *create ()
+  {
+    hb_paint_funcs_t *funcs = hb_paint_funcs_create ();
+
+    hb_paint_funcs_set_push_transform_func (funcs, hb_cairo_push_transform, nullptr, nullptr);
+    hb_paint_funcs_set_pop_transform_func (funcs, hb_cairo_pop_transform, nullptr, nullptr);
+    hb_paint_funcs_set_push_clip_glyph_func (funcs, hb_cairo_push_clip_glyph, nullptr, nullptr);
+    hb_paint_funcs_set_push_clip_rectangle_func (funcs, hb_cairo_push_clip_rectangle, nullptr, nullptr);
+    hb_paint_funcs_set_pop_clip_func (funcs, hb_cairo_pop_clip, nullptr, nullptr);
+    hb_paint_funcs_set_push_group_func (funcs, hb_cairo_push_group, nullptr, nullptr);
+    hb_paint_funcs_set_pop_group_func (funcs, hb_cairo_pop_group, nullptr, nullptr);
+    hb_paint_funcs_set_color_func (funcs, hb_cairo_paint_color, nullptr, nullptr);
+    hb_paint_funcs_set_image_func (funcs, hb_cairo_paint_image, nullptr, nullptr);
+    hb_paint_funcs_set_linear_gradient_func (funcs, hb_cairo_paint_linear_gradient, nullptr, nullptr);
+    hb_paint_funcs_set_radial_gradient_func (funcs, hb_cairo_paint_radial_gradient, nullptr, nullptr);
+    hb_paint_funcs_set_sweep_gradient_func (funcs, hb_cairo_paint_sweep_gradient, nullptr, nullptr);
+    hb_paint_funcs_set_custom_palette_color_func (funcs, hb_cairo_paint_custom_palette_color, nullptr, nullptr);
+
+    hb_paint_funcs_make_immutable (funcs);
+
+    hb_atexit (free_static_cairo_paint_funcs);
+
+    return funcs;
+  }
+} static_cairo_paint_funcs;
+
+static inline
+void free_static_cairo_paint_funcs ()
+{
+  static_cairo_paint_funcs.free_instance ();
+}
+
+static hb_paint_funcs_t *
+hb_cairo_paint_get_funcs ()
+{
+  return static_cairo_paint_funcs.get_unconst ();
+}
+#endif
+
+static const cairo_user_data_key_t hb_cairo_face_user_data_key = {0};
+static const cairo_user_data_key_t hb_cairo_font_user_data_key = {0};
+static const cairo_user_data_key_t hb_cairo_font_init_func_user_data_key = {0};
+static const cairo_user_data_key_t hb_cairo_font_init_user_data_user_data_key = {0};
+static const cairo_user_data_key_t hb_cairo_scale_factor_user_data_key = {0};
+
+static void hb_cairo_face_destroy (void *p) { hb_face_destroy ((hb_face_t *) p); }
+static void hb_cairo_font_destroy (void *p) { hb_font_destroy ((hb_font_t *) p); }
+
+static cairo_status_t
+hb_cairo_init_scaled_font (cairo_scaled_font_t  *scaled_font,
+			   cairo_t              *cr HB_UNUSED,
+			   cairo_font_extents_t *extents)
+{
+  cairo_font_face_t *font_face = cairo_scaled_font_get_font_face (scaled_font);
+
+  hb_font_t *font = (hb_font_t *) cairo_font_face_get_user_data (font_face,
+								 &hb_cairo_font_user_data_key);
+
+  if (!font)
+  {
+    hb_face_t *face = (hb_face_t *) cairo_font_face_get_user_data (font_face,
+								   &hb_cairo_face_user_data_key);
+    font = hb_font_create (face);
+
+#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1,16,0)
+    cairo_font_options_t *font_options = cairo_font_options_create ();
+
+    // Set variations
+    cairo_scaled_font_get_font_options (scaled_font, font_options);
+    const char *variations = cairo_font_options_get_variations (font_options);
+    hb_vector_t<hb_variation_t> vars;
+    const char *p = variations;
+    while (p && *p)
+    {
+      const char *end = strpbrk ((char *) p, ", ");
+      hb_variation_t var;
+      if (hb_variation_from_string (p, end ? end - p : -1, &var))
+	vars.push (var);
+      p = end ? end + 1 : nullptr;
+    }
+    hb_font_set_variations (font, &vars[0], vars.length);
+
+    cairo_font_options_destroy (font_options);
+#endif
+
+    // Set scale; Note: should NOT set slant, or we'll double-slant.
+    unsigned scale_factor = hb_cairo_font_face_get_scale_factor (font_face);
+    if (scale_factor)
+    {
+      cairo_matrix_t font_matrix;
+      cairo_scaled_font_get_scale_matrix (scaled_font, &font_matrix);
+      hb_font_set_scale (font,
+			 round (font_matrix.xx * scale_factor),
+			 round (font_matrix.yy * scale_factor));
+    }
+
+    auto *init_func = (hb_cairo_font_init_func_t)
+		      cairo_font_face_get_user_data (font_face,
+						     &hb_cairo_font_init_func_user_data_key);
+    if (init_func)
+    {
+      void *user_data = cairo_font_face_get_user_data (font_face,
+						       &hb_cairo_font_init_user_data_user_data_key);
+      font = init_func (font, scaled_font, user_data);
+    }
+
+    hb_font_make_immutable (font);
+  }
+
+  cairo_scaled_font_set_user_data (scaled_font,
+				   &hb_cairo_font_user_data_key,
+				   (void *) hb_font_reference (font),
+				   hb_cairo_font_destroy);
+
+  hb_position_t x_scale, y_scale;
+  hb_font_get_scale (font, &x_scale, &y_scale);
+
+  hb_font_extents_t hb_extents;
+  hb_font_get_h_extents (font, &hb_extents);
+
+  extents->ascent  = (double)  hb_extents.ascender  / y_scale;
+  extents->descent = (double) -hb_extents.descender / y_scale;
+  extents->height  = extents->ascent + extents->descent;
+
+#ifdef HAVE_CAIRO_USER_FONT_FACE_SET_RENDER_COLOR_GLYPH_FUNC
+  hb_map_t *color_cache = hb_map_create ();
+  if (unlikely (CAIRO_STATUS_SUCCESS != cairo_scaled_font_set_user_data (scaled_font,
+									 &color_cache_key,
+									 color_cache,
+									 _hb_cairo_destroy_map)))
+    hb_map_destroy (color_cache);
+#endif
+
+  return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+hb_cairo_text_to_glyphs (cairo_scaled_font_t        *scaled_font,
+			 const char	            *utf8,
+			 int		             utf8_len,
+			 cairo_glyph_t	           **glyphs,
+			 int		            *num_glyphs,
+			 cairo_text_cluster_t      **clusters,
+			 int		            *num_clusters,
+			 cairo_text_cluster_flags_t *cluster_flags)
+{
+  hb_font_t *font = (hb_font_t *) cairo_scaled_font_get_user_data (scaled_font,
+								   &hb_cairo_font_user_data_key);
+
+  hb_buffer_t *buffer = hb_buffer_create ();
+  hb_buffer_add_utf8 (buffer, utf8, utf8_len, 0, utf8_len);
+  hb_buffer_guess_segment_properties (buffer);
+  hb_shape (font, buffer, nullptr, 0);
+
+  hb_cairo_glyphs_from_buffer (buffer,
+			       true,
+			       font->x_scale, font->y_scale,
+			       0., 0.,
+			       utf8, utf8_len,
+			       glyphs, (unsigned *) num_glyphs,
+			       clusters, (unsigned *) num_clusters,
+			       cluster_flags);
+
+  hb_buffer_destroy (buffer);
+
+  return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+hb_cairo_render_glyph (cairo_scaled_font_t  *scaled_font,
+		       unsigned long         glyph,
+		       cairo_t              *cr,
+		       cairo_text_extents_t *extents)
+{
+  hb_font_t *font = (hb_font_t *) cairo_scaled_font_get_user_data (scaled_font,
+								   &hb_cairo_font_user_data_key);
+
+  hb_position_t x_scale, y_scale;
+  hb_font_get_scale (font, &x_scale, &y_scale);
+  cairo_scale (cr, +1./x_scale, -1./y_scale);
+
+  hb_font_draw_glyph (font, glyph, hb_cairo_draw_get_funcs (), cr);
+
+  cairo_fill (cr);
+
+  return CAIRO_STATUS_SUCCESS;
+}
+
+#ifdef HAVE_CAIRO_USER_FONT_FACE_SET_RENDER_COLOR_GLYPH_FUNC
+
+static cairo_status_t
+hb_cairo_render_color_glyph (cairo_scaled_font_t  *scaled_font,
+			     unsigned long         glyph,
+			     cairo_t              *cr,
+			     cairo_text_extents_t *extents)
+{
+  hb_font_t *font = (hb_font_t *) cairo_scaled_font_get_user_data (scaled_font,
+								   &hb_cairo_font_user_data_key);
+
+  unsigned int palette = 0;
+#ifdef CAIRO_COLOR_PALETTE_DEFAULT
+  cairo_font_options_t *options = cairo_font_options_create ();
+  cairo_scaled_font_get_font_options (scaled_font, options);
+  palette = cairo_font_options_get_color_palette (options);
+  cairo_font_options_destroy (options);
+#endif
+
+  hb_color_t color = HB_COLOR (0, 0, 0, 255);
+  hb_position_t x_scale, y_scale;
+  hb_font_get_scale (font, &x_scale, &y_scale);
+  cairo_scale (cr, +1./x_scale, -1./y_scale);
+
+  hb_cairo_context_t c;
+  c.scaled_font = scaled_font;
+  c.cr = cr;
+  c.color_cache = (hb_map_t *) cairo_scaled_font_get_user_data (scaled_font, &color_cache_key);
+
+  hb_font_paint_glyph (font, glyph, hb_cairo_paint_get_funcs (), &c, palette, color);
+
+
+  return CAIRO_STATUS_SUCCESS;
+}
+
+#endif
+
+static cairo_font_face_t *
+user_font_face_create (hb_face_t *face)
+{
+  cairo_font_face_t *cairo_face;
+
+  cairo_face = cairo_user_font_face_create ();
+  cairo_user_font_face_set_init_func (cairo_face, hb_cairo_init_scaled_font);
+  cairo_user_font_face_set_text_to_glyphs_func (cairo_face, hb_cairo_text_to_glyphs);
+  cairo_user_font_face_set_render_glyph_func (cairo_face, hb_cairo_render_glyph);
+#ifdef HAVE_CAIRO_USER_FONT_FACE_SET_RENDER_COLOR_GLYPH_FUNC
+  if (hb_ot_color_has_png (face) || hb_ot_color_has_layers (face) || hb_ot_color_has_paint (face))
+    cairo_user_font_face_set_render_color_glyph_func (cairo_face, hb_cairo_render_color_glyph);
+#endif
+
+  if (unlikely (CAIRO_STATUS_SUCCESS != cairo_font_face_set_user_data (cairo_face,
+								       &hb_cairo_face_user_data_key,
+								       (void *) hb_face_reference (face),
+								       hb_cairo_face_destroy)))
+    hb_face_destroy (face);
+
+  return cairo_face;
+}
+
+/**
+ * hb_cairo_font_face_create_for_font:
+ * @font: a #hb_font_t
+ *
+ * Creates a #cairo_font_face_t for rendering text according
+ * to @font.
+ *
+ * Note that the scale of @font does not affect the rendering,
+ * but the variations and slant that are set on @font do.
+ *
+ * Returns: (transfer full): a newly created #cairo_font_face_t
+ *
+ * Since: 7.0.0
+ */
+cairo_font_face_t *
+hb_cairo_font_face_create_for_font (hb_font_t *font)
+{
+  hb_font_make_immutable (font);
+
+  auto *cairo_face =  user_font_face_create (font->face);
+
+  if (unlikely (CAIRO_STATUS_SUCCESS != cairo_font_face_set_user_data (cairo_face,
+								       &hb_cairo_font_user_data_key,
+								       (void *) hb_font_reference (font),
+								       hb_cairo_font_destroy)))
+    hb_font_destroy (font);
+
+  return cairo_face;
+}
+
+/**
+ * hb_cairo_font_face_get_font:
+ * @font_face: a #cairo_font_face_t
+ *
+ * Gets the #hb_font_t that @font_face was created from.
+ *
+ * Returns: (nullable) (transfer none): the #hb_font_t that @font_face was created from
+ *
+ * Since: 7.0.0
+ */
+hb_font_t *
+hb_cairo_font_face_get_font (cairo_font_face_t *font_face)
+{
+  return (hb_font_t *) cairo_font_face_get_user_data (font_face,
+						      &hb_cairo_font_user_data_key);
+}
+
+/**
+ * hb_cairo_font_face_create_for_face:
+ * @face: a #hb_face_t
+ *
+ * Creates a #cairo_font_face_t for rendering text according
+ * to @face.
+ *
+ * Returns: (transfer full): a newly created #cairo_font_face_t
+ *
+ * Since: 7.0.0
+ */
+cairo_font_face_t *
+hb_cairo_font_face_create_for_face (hb_face_t *face)
+{
+  hb_face_make_immutable (face);
+
+  return user_font_face_create (face);
+}
+
+/**
+ * hb_cairo_font_face_get_face:
+ * @font_face: a #cairo_font_face_t
+ *
+ * Gets the #hb_face_t associated with @font_face.
+ *
+ * Returns: (nullable) (transfer none): the #hb_face_t associated with @font_face
+ *
+ * Since: 7.0.0
+ */
+hb_face_t *
+hb_cairo_font_face_get_face (cairo_font_face_t *font_face)
+{
+  return (hb_face_t *) cairo_font_face_get_user_data (font_face,
+						      &hb_cairo_face_user_data_key);
+}
+
+/**
+ * hb_cairo_font_face_set_font_init_func:
+ * @font_face: a #cairo_font_face_t
+ * @func: The virtual method to use
+ * @user_data: user data accompanying the method
+ * @destroy: function to call when @user_data is not needed anymore
+ *
+ * Set the virtual method to be called when a cairo
+ * face created using hb_cairo_font_face_create_for_face()
+ * creates an #hb_font_t for a #cairo_scaled_font_t.
+ *
+ * Since: 7.0.0
+ */
+void
+hb_cairo_font_face_set_font_init_func (cairo_font_face_t *font_face,
+				       hb_cairo_font_init_func_t func,
+				       void *user_data,
+				       hb_destroy_func_t destroy)
+{
+  cairo_font_face_set_user_data (font_face,
+				 &hb_cairo_font_init_func_user_data_key,
+				 (void *) func,
+				 nullptr);
+  if (unlikely (CAIRO_STATUS_SUCCESS != cairo_font_face_set_user_data (font_face,
+								       &hb_cairo_font_init_user_data_user_data_key,
+								       (void *) user_data,
+								       destroy)) && destroy)
+  {
+    destroy (user_data);
+    cairo_font_face_set_user_data (font_face,
+				   &hb_cairo_font_init_func_user_data_key,
+				   nullptr,
+				   nullptr);
+  }
+}
+
+/**
+ * hb_cairo_scaled_font_get_font:
+ * @scaled_font: a #cairo_scaled_font_t
+ *
+ * Gets the #hb_font_t associated with @scaled_font.
+ *
+ * Returns: (nullable) (transfer none): the #hb_font_t associated with @scaled_font
+ *
+ * Since: 7.0.0
+ */
+hb_font_t *
+hb_cairo_scaled_font_get_font (cairo_scaled_font_t *scaled_font)
+{
+  return (hb_font_t *) cairo_scaled_font_get_user_data (scaled_font, &hb_cairo_font_user_data_key);
+}
+
+
+/**
+ * hb_cairo_font_face_set_scale_factor:
+ * @scale_factor: The scale factor to use. See below
+ * @font_face: a #cairo_font_face_t
+ *
+ * Sets the scale factor of the @font_face. Default scale
+ * factor is zero.
+ *
+ * When a #cairo_font_face_t is created from a #hb_face_t using
+ * hb_cairo_font_face_create_for_face(), such face will create
+ * #hb_font_t objects during scaled-font creation.  The scale
+ * factor defines how the scale set on such #hb_font_t objects
+ * relates to the font-matrix (as such font size) of the cairo
+ * scaled-font.
+ *
+ * If the scale-factor is zero (default), then the scale of the
+ * #hb_font_t object will be left at default, which is the UPEM
+ * value of the respective #hb_face_t.
+ *
+ * If the scale-factor is set to non-zero, then the X and Y scale
+ * of the #hb_font_t object will be respectively set to the
+ * @scale_factor times the xx and yy elements of the scale-matrix
+ * of the cairo scaled-font being created.
+ *
+ * When using the hb_cairo_glyphs_from_buffer() API to convert the
+ * HarfBuzz glyph buffer that resulted from shaping with such a #hb_font_t,
+ * if the scale-factor was non-zero, you can pass it directly to
+ * that API as both X and Y scale factors.
+ *
+ * If the scale-factor was zero however, or the cairo face was
+ * created using the alternative constructor
+ * hb_cairo_font_face_create_for_font(), you need to calculate the
+ * correct X/Y scale-factors to pass to hb_cairo_glyphs_from_buffer()
+ * by dividing the #hb_font_t X/Y scale-factors by the
+ * cairo scaled-font's scale-matrix XX/YY components respectively
+ * and use those values.  Or if you know that relationship offhand
+ * (because you set the scale of the #hb_font_t yourself), use
+ * the conversion rate involved.
+ *
+ * Since: 7.0.0
+ */
+void
+hb_cairo_font_face_set_scale_factor (cairo_font_face_t *font_face,
+				     unsigned int scale_factor)
+{
+  cairo_font_face_set_user_data (font_face,
+				 &hb_cairo_scale_factor_user_data_key,
+				 (void *) (uintptr_t) scale_factor,
+				 nullptr);
+}
+
+/**
+ * hb_cairo_font_face_get_scale_factor:
+ * @font_face: a #cairo_font_face_t
+ *
+ * Gets the scale factor set on the @font_face. Defaults to zero.
+ * See hb_cairo_font_face_set_scale_factor() for details.
+ *
+ * Returns: the scale factor of @font_face
+ *
+ * Since: 7.0.0
+ */
+unsigned int
+hb_cairo_font_face_get_scale_factor (cairo_font_face_t *font_face)
+{
+  return (unsigned int) (uintptr_t)
+	 cairo_font_face_get_user_data (font_face,
+					&hb_cairo_scale_factor_user_data_key);
+}
+
+
+/**
+ * hb_cairo_glyphs_from_buffer:
+ * @buffer: a #hb_buffer_t containing glyphs
+ * @utf8_clusters: `true` if @buffer clusters are in bytes, instead of characters
+ * @x_scale_factor: scale factor to divide #hb_position_t Y values by
+ * @y_scale_factor: scale factor to divide #hb_position_t X values by
+ * @x: X position to place first glyph
+ * @y: Y position to place first glyph
+ * @utf8: (nullable): the text that was shaped in @buffer
+ * @utf8_len: the length of @utf8 in bytes
+ * @glyphs: (out): return location for an array of #cairo_glyph_t
+ * @num_glyphs: (inout): return location for the length of @glyphs
+ * @clusters: (out) (nullable): return location for an array of cluster positions
+ * @num_clusters: (inout) (nullable): return location for the length of @clusters
+ * @cluster_flags: (out) (nullable): return location for cluster flags
+ *
+ * Extracts information from @buffer in a form that can be
+ * passed to cairo_show_text_glyphs() or cairo_show_glyphs().
+ * This API is modeled after cairo_scaled_font_text_to_glyphs() and
+ * cairo_user_scaled_font_text_to_glyphs_func_t.
+ *
+ * The @num_glyphs argument should be preset to the number of glyph entries available
+ * in the @glyphs buffer. If the @glyphs buffer is `NULL`, the value of
+ * @num_glyphs must be zero.  If the provided glyph array is too short for
+ * the conversion (or for convenience), a new glyph array may be allocated
+ * using cairo_glyph_allocate() and placed in @glyphs.  Upon return,
+ * @num_glyphs should contain the number of generated glyphs.  If the value
+ * @glyphs points at has changed after the call, the caller will free the
+ * allocated glyph array using cairo_glyph_free().  The caller will also free
+ * the original value of @glyphs, so this function shouldn't do so.
+ *
+ * If @clusters is not `NULL`, then @num_clusters and @cluster_flags
+ * should not be either, and @utf8 must be provided, and cluster
+ * mapping will be computed. The semantics of how
+ * cluster array allocation works is similar to the glyph array.  That is,
+ * if @clusters initially points to a non-`NULL` value, that array may be used
+ * as a cluster buffer, and @num_clusters points to the number of cluster
+ * entries available there.  If the provided cluster array is too short for
+ * the conversion (or for convenience), a new cluster array may be allocated
+ * using cairo_text_cluster_allocate() and placed in @clusters.  In this case,
+ * the original value of @clusters will still be freed by the caller.  Upon
+ * return, @num_clusters will contain the number of generated clusters.
+ * If the value @clusters points at has changed after the call, the caller
+ * will free the allocated cluster array using cairo_text_cluster_free().
+ *
+ * See hb_cairo_font_face_set_scale_factor() for the details of
+ * the @scale_factor argument.
+ *
+ * The returned @glyphs vector actually has `@num_glyphs + 1` entries in
+ * it and the x,y values of the extra entry at the end add up the advance
+ * x,y of all the glyphs in the @buffer.
+ *
+ * Since: 7.0.0
+ */
+void
+hb_cairo_glyphs_from_buffer (hb_buffer_t *buffer,
+			     hb_bool_t utf8_clusters,
+			     double x_scale_factor,
+			     double y_scale_factor,
+			     double x,
+			     double y,
+			     const char *utf8,
+			     int utf8_len,
+			     cairo_glyph_t **glyphs,
+			     unsigned int *num_glyphs,
+			     cairo_text_cluster_t **clusters,
+			     unsigned int *num_clusters,
+			     cairo_text_cluster_flags_t *cluster_flags)
+{
+  if (utf8 && utf8_len < 0)
+    utf8_len = strlen (utf8);
+
+  unsigned orig_num_glyphs = *num_glyphs;
+  *num_glyphs = hb_buffer_get_length (buffer);
+  hb_glyph_info_t *hb_glyph = hb_buffer_get_glyph_infos (buffer, nullptr);
+  hb_glyph_position_t *hb_position = hb_buffer_get_glyph_positions (buffer, nullptr);
+  if (orig_num_glyphs < *num_glyphs + 1)
+    *glyphs = cairo_glyph_allocate (*num_glyphs + 1);
+
+  if (clusters && utf8)
+  {
+    unsigned orig_num_clusters = *num_clusters;
+    *num_clusters = *num_glyphs ? 1 : 0;
+    for (unsigned int i = 1; i < *num_glyphs; i++)
+      if (hb_glyph[i].cluster != hb_glyph[i-1].cluster)
+	(*num_clusters)++;
+    if (orig_num_clusters < *num_clusters)
+      *clusters = cairo_text_cluster_allocate (*num_clusters);
+  }
+
+  double x_scale = x_scale_factor ? 1. / x_scale_factor : 0.;
+  double y_scale = y_scale_factor ? 1. / y_scale_factor : 0.;
+  hb_position_t hx = 0, hy = 0;
+  int i;
+  for (i = 0; i < (int) *num_glyphs; i++)
+  {
+    (*glyphs)[i].index = hb_glyph[i].codepoint;
+    (*glyphs)[i].x = x + (+hb_position->x_offset + hx) * x_scale;
+    (*glyphs)[i].y = y + (-hb_position->y_offset + hy) * y_scale;
+    hx +=  hb_position->x_advance;
+    hy += -hb_position->y_advance;
+
+    hb_position++;
+  }
+  (*glyphs)[i].index = -1;
+  (*glyphs)[i].x = round (hx * x_scale);
+  (*glyphs)[i].y = round (hy * y_scale);
+
+  if (clusters && *num_clusters && utf8)
+  {
+    memset ((void *) *clusters, 0, *num_clusters * sizeof ((*clusters)[0]));
+    hb_bool_t backward = HB_DIRECTION_IS_BACKWARD (hb_buffer_get_direction (buffer));
+    *cluster_flags = backward ? CAIRO_TEXT_CLUSTER_FLAG_BACKWARD : (cairo_text_cluster_flags_t) 0;
+    unsigned int cluster = 0;
+    const char *start = utf8, *end;
+    (*clusters)[cluster].num_glyphs++;
+    if (backward)
+    {
+      for (i = *num_glyphs - 2; i >= 0; i--)
+      {
+	if (hb_glyph[i].cluster != hb_glyph[i+1].cluster)
+	{
+	  assert (hb_glyph[i].cluster > hb_glyph[i+1].cluster);
+	  if (utf8_clusters)
+	    end = start + hb_glyph[i].cluster - hb_glyph[i+1].cluster;
+	  else
+	    end = (const char *) hb_utf_offset_to_pointer<hb_utf8_t> ((const uint8_t *) start,
+								      (signed) (hb_glyph[i].cluster - hb_glyph[i+1].cluster));
+	  (*clusters)[cluster].num_bytes = end - start;
+	  start = end;
+	  cluster++;
+	}
+	(*clusters)[cluster].num_glyphs++;
+      }
+      (*clusters)[cluster].num_bytes = utf8 + utf8_len - start;
+    }
+    else
+    {
+      for (i = 1; i < (int) *num_glyphs; i++)
+      {
+	if (hb_glyph[i].cluster != hb_glyph[i-1].cluster)
+	{
+	  assert (hb_glyph[i].cluster > hb_glyph[i-1].cluster);
+	  if (utf8_clusters)
+	    end = start + hb_glyph[i].cluster - hb_glyph[i-1].cluster;
+	  else
+	    end = (const char *) hb_utf_offset_to_pointer<hb_utf8_t> ((const uint8_t *) start,
+								      (signed) (hb_glyph[i].cluster - hb_glyph[i-1].cluster));
+	  (*clusters)[cluster].num_bytes = end - start;
+	  start = end;
+	  cluster++;
+	}
+	(*clusters)[cluster].num_glyphs++;
+      }
+      (*clusters)[cluster].num_bytes = utf8 + utf8_len - start;
+    }
+  }
+  else if (num_clusters)
+    *num_clusters = 0;
+}
+
+#endif

+ 99 - 0
thirdparty/harfbuzz/src/hb-cairo.h

@@ -0,0 +1,99 @@
+/*
+ * Copyright © 2022 Red Hat, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Matthias Clasen
+ */
+
+#ifndef HB_CAIRO_H
+#define HB_CAIRO_H
+
+#include "hb.h"
+
+#include <cairo.h>
+
+HB_BEGIN_DECLS
+
+HB_EXTERN cairo_font_face_t *
+hb_cairo_font_face_create_for_font (hb_font_t *font);
+
+HB_EXTERN hb_font_t *
+hb_cairo_font_face_get_font (cairo_font_face_t *font_face);
+
+HB_EXTERN cairo_font_face_t *
+hb_cairo_font_face_create_for_face (hb_face_t *face);
+
+HB_EXTERN hb_face_t *
+hb_cairo_font_face_get_face (cairo_font_face_t *font_face);
+
+/**
+ * hb_cairo_font_init_func_t:
+ * @font: The #hb_font_t being created
+ * @scaled_font: The respective #cairo_scaled_font_t
+ * @user_data: User data accompanying this method
+ *
+ * The type of a virtual method to be called when a cairo
+ * face created using hb_cairo_font_face_create_for_face()
+ * creates an #hb_font_t for a #cairo_scaled_font_t.
+ *
+ * Return value: the #hb_font_t value to use; in most cases same as @font
+ *
+ * Since: 7.0.0
+ */
+typedef hb_font_t * (*hb_cairo_font_init_func_t) (hb_font_t *font,
+						  cairo_scaled_font_t *scaled_font,
+						  void *user_data);
+
+HB_EXTERN void
+hb_cairo_font_face_set_font_init_func (cairo_font_face_t *font_face,
+				       hb_cairo_font_init_func_t func,
+				       void *user_data,
+				       hb_destroy_func_t destroy);
+
+HB_EXTERN hb_font_t *
+hb_cairo_scaled_font_get_font (cairo_scaled_font_t *scaled_font);
+
+HB_EXTERN void
+hb_cairo_font_face_set_scale_factor (cairo_font_face_t *font_face,
+				     unsigned int scale_factor);
+
+HB_EXTERN unsigned int
+hb_cairo_font_face_get_scale_factor (cairo_font_face_t *font_face);
+
+HB_EXTERN void
+hb_cairo_glyphs_from_buffer (hb_buffer_t *buffer,
+			     hb_bool_t utf8_clusters,
+			     double x_scale_factor,
+			     double y_scale_factor,
+			     double x,
+			     double y,
+			     const char *utf8,
+			     int utf8_len,
+			     cairo_glyph_t **glyphs,
+			     unsigned int *num_glyphs,
+			     cairo_text_cluster_t **clusters,
+			     unsigned int *num_clusters,
+			     cairo_text_cluster_flags_t *cluster_flags);
+
+HB_END_DECLS
+
+#endif /* HB_CAIRO_H */

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

@@ -488,7 +488,7 @@ struct op_str_t
 
   const unsigned char *ptr = nullptr;
 
-  op_code_t  op;
+  op_code_t  op = OpCode_Invalid;
 
   uint8_t length = 0;
 };
@@ -522,20 +522,10 @@ struct parsed_values_t
 
   void alloc (unsigned n)
   {
-    values.alloc (n);
+    values.alloc (n, true);
   }
 
-  void add_op (op_code_t op, const byte_str_ref_t& str_ref = byte_str_ref_t ())
-  {
-    VAL *val = values.push ();
-    val->op = op;
-    auto arr = str_ref.sub_array (opStart, str_ref.get_offset () - opStart);
-    val->ptr = arr.arrayZ;
-    val->length = arr.length;
-    opStart = str_ref.get_offset ();
-  }
-
-  void add_op (op_code_t op, const byte_str_ref_t& str_ref, const VAL &v)
+  void add_op (op_code_t op, const byte_str_ref_t& str_ref = byte_str_ref_t (), const VAL &v = VAL ())
   {
     VAL *val = values.push (v);
     val->op = op;

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

@@ -57,7 +57,6 @@ struct call_context_t
 
 /* call stack */
 const unsigned int kMaxCallLimit = 10;
-const unsigned int kMaxOps = 10000;
 struct call_stack_t : cff_stack_t<call_context_t, kMaxCallLimit> {};
 
 template <typename SUBRS>
@@ -882,7 +881,7 @@ struct cs_interpreter_t : interpreter_t<ENV>
   {
     SUPER::env.set_endchar (false);
 
-    unsigned max_ops = kMaxOps;
+    unsigned max_ops = HB_CFF_MAX_OPS;
     for (;;) {
       if (unlikely (!--max_ops))
       {

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

@@ -35,10 +35,8 @@ using namespace OT;
 /* an opstr and the parsed out dict value(s) */
 struct dict_val_t : op_str_t
 {
-  void init () { single_val.set_int (0); }
+  void init () {}
   void fini () {}
-
-  number_t	      single_val;
 };
 
 typedef dict_val_t num_dict_val_t;

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

@@ -38,7 +38,8 @@ typedef biased_subrs_t<CFF1Subrs>   cff1_biased_subrs_t;
 struct cff1_cs_interp_env_t : cs_interp_env_t<number_t, CFF1Subrs>
 {
   template <typename ACC>
-  cff1_cs_interp_env_t (const hb_ubytes_t &str, ACC &acc, unsigned int fd)
+  cff1_cs_interp_env_t (const hb_ubytes_t &str, ACC &acc, unsigned int fd,
+			const int *coords_=nullptr, unsigned int num_coords_=0)
     : SUPER (str, acc.globalSubrs, acc.privateDicts[fd].localSubrs)
   {
     processed_width = false;

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

@@ -45,7 +45,7 @@ struct blend_arg_t : number_t
     numValues = numValues_;
     valueIndex = valueIndex_;
     unsigned numBlends = blends_.length;
-    if (unlikely (!deltas.resize (numBlends)))
+    if (unlikely (!deltas.resize_exact (numBlends)))
       return;
     for (unsigned int i = 0; i < numBlends; i++)
       deltas.arrayZ[i] = blends_.arrayZ[i];
@@ -55,7 +55,7 @@ struct blend_arg_t : number_t
   void reset_blends ()
   {
     numValues = valueIndex = 0;
-    deltas.resize (0);
+    deltas.shrink (0);
   }
 
   unsigned int numValues;
@@ -118,7 +118,7 @@ struct cff2_cs_interp_env_t : cs_interp_env_t<ELEM, CFF2Subrs>
       region_count = varStore->varStore.get_region_index_count (get_ivs ());
       if (do_blend)
       {
-	if (unlikely (!scalars.resize (region_count)))
+	if (unlikely (!scalars.resize_exact (region_count)))
 	  SUPER::set_error ();
 	else
 	  varStore->varStore.get_region_scalars (get_ivs (), coords, num_coords,
@@ -163,6 +163,8 @@ struct cff2_cs_interp_env_t : cs_interp_env_t<ELEM, CFF2Subrs>
     return v;
   }
 
+  bool have_coords () const { return num_coords; }
+
   protected:
   const int     *coords;
   unsigned int  num_coords;
@@ -222,7 +224,10 @@ struct cff2_cs_opset_t : cs_opset_t<ELEM, OPSET, cff2_cs_interp_env_t<ELEM>, PAR
 				 const hb_array_t<const ELEM> blends,
 				 unsigned n, unsigned i)
   {
-    arg.set_blends (n, i, blends);
+    if (env.have_coords ())
+      arg.set_int (round (arg.to_real () + env.blend_deltas (blends)));
+    else
+      arg.set_blends (n, i, blends);
   }
   template <typename T = ELEM,
 	    hb_enable_if (!hb_is_same (T, blend_arg_t))>

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

@@ -29,32 +29,6 @@
 #include "hb.hh"
 #include "hb-machinery.hh"
 
-#if !defined(HB_NO_SETLOCALE) && (!defined(HAVE_NEWLOCALE) || !defined(HAVE_USELOCALE))
-#define HB_NO_SETLOCALE 1
-#endif
-
-#ifndef HB_NO_SETLOCALE
-
-#include <locale.h>
-#ifdef HAVE_XLOCALE_H
-#include <xlocale.h> // Needed on BSD/OS X for uselocale
-#endif
-
-#ifdef WIN32
-#define hb_locale_t _locale_t
-#else
-#define hb_locale_t locale_t
-#endif
-#define hb_setlocale setlocale
-#define hb_uselocale uselocale
-
-#else
-
-#define hb_locale_t void *
-#define hb_setlocale(Category, Locale) "C"
-#define hb_uselocale(Locale) ((hb_locale_t) 0)
-
-#endif
 
 /**
  * SECTION:hb-common

+ 26 - 0
thirdparty/harfbuzz/src/hb-common.h

@@ -897,6 +897,32 @@ HB_EXTERN uint8_t
 hb_color_get_blue (hb_color_t color);
 #define hb_color_get_blue(color)	(((color) >> 24) & 0xFF)
 
+/**
+ * hb_glyph_extents_t:
+ * @x_bearing: Distance from the x-origin to the left extremum of the glyph.
+ * @y_bearing: Distance from the top extremum of the glyph to the y-origin.
+ * @width: Distance from the left extremum of the glyph to the right extremum.
+ * @height: Distance from the top extremum of the glyph to the bottom extremum.
+ *
+ * Glyph extent values, measured in font units.
+ *
+ * Note that @height is negative, in coordinate systems that grow up.
+ **/
+typedef struct hb_glyph_extents_t {
+  hb_position_t x_bearing;
+  hb_position_t y_bearing;
+  hb_position_t width;
+  hb_position_t height;
+} hb_glyph_extents_t;
+
+/**
+ * hb_font_t:
+ *
+ * Data type for holding fonts.
+ *
+ */
+typedef struct hb_font_t hb_font_t;
+
 HB_END_DECLS
 
 #endif /* HB_COMMON_H */

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

@@ -37,6 +37,7 @@
 
 #ifndef HB_EXPERIMENTAL_API
 #define HB_NO_BEYOND_64K
+#define HB_NO_CUBIC_GLYF
 #define HB_NO_VAR_COMPOSITES
 #endif
 
@@ -80,9 +81,10 @@
 #define HB_NO_MMAP
 #define HB_NO_NAME
 #define HB_NO_OPEN
-#define HB_NO_SETLOCALE
 #define HB_NO_OT_FONT_GLYPH_NAMES
 #define HB_NO_OT_SHAPE_FRACTIONS
+#define HB_NO_PAINT
+#define HB_NO_SETLOCALE
 #define HB_NO_STYLE
 #define HB_NO_SUBSET_LAYOUT
 #define HB_NO_VERTICAL
@@ -134,6 +136,10 @@
 #define HB_NO_SUBSET_CFF
 #endif
 
+#ifdef HB_NO_DRAW
+#define HB_NO_OUTLINE
+#endif
+
 #ifdef HB_NO_GETENV
 #define HB_NO_UNISCRIBE_BUG_COMPATIBLE
 #endif

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

@@ -511,7 +511,6 @@ _hb_coretext_shape (hb_shape_plan_t    *shape_plan,
 	buffer->merge_clusters (i - 1, i + 1);
   }
 
-  hb_vector_t<feature_record_t> feature_records;
   hb_vector_t<range_record_t> range_records;
 
   /*

+ 2 - 0
thirdparty/harfbuzz/src/hb-cplusplus.hh

@@ -160,6 +160,8 @@ HB_DEFINE_VTABLE (map);
 HB_DEFINE_VTABLE (set);
 HB_DEFINE_VTABLE (shape_plan);
 HB_DEFINE_VTABLE (unicode_funcs);
+HB_DEFINE_VTABLE (draw_funcs);
+HB_DEFINE_VTABLE (paint_funcs);
 
 #undef HB_DEFINE_VTABLE
 

+ 10 - 6
thirdparty/harfbuzz/src/hb-debug.hh

@@ -113,7 +113,7 @@ _hb_print_func (const char *func)
     const char *paren = strchr (func, '(');
     if (paren)
       func_len = paren - func;
-    fprintf (stderr, "%.*s", func_len, func);
+    fprintf (stderr, "%.*s", (int) func_len, func);
   }
 }
 
@@ -142,9 +142,9 @@ _hb_debug_msg_va (const char *what,
   fprintf (stderr, "%-10s", what ? what : "");
 
   if (obj)
-    fprintf (stderr, "(%*p) ", (unsigned int) (2 * sizeof (void *)), obj);
+    fprintf (stderr, "(%*p) ", (int) (2 * sizeof (void *)), obj);
   else
-    fprintf (stderr, " %*s  ", (unsigned int) (2 * sizeof (void *)), "");
+    fprintf (stderr, " %*s  ", (int) (2 * sizeof (void *)), "");
 
   if (indented) {
 #define VBAR	"\342\224\202"	/* U+2502 BOX DRAWINGS LIGHT VERTICAL */
@@ -306,7 +306,7 @@ struct hb_auto_trace_t
     }
 
     _hb_debug_msg<max_level> (what, obj, func, true, plevel ? *plevel : 1, -1,
-			      "return %s (line %d)",
+			      "return %s (line %u)",
 			      hb_printer_t<hb_decay<decltype (v)>>().print (v), line);
     if (plevel) --*plevel;
     plevel = nullptr;
@@ -373,6 +373,10 @@ struct hb_no_trace_t {
 #define HB_DEBUG_FT (HB_DEBUG+0)
 #endif
 
+#ifndef HB_DEBUG_JUSTIFY
+#define HB_DEBUG_JUSTIFY (HB_DEBUG+0)
+#endif
+
 #ifndef HB_DEBUG_OBJECT
 #define HB_DEBUG_OBJECT (HB_DEBUG+0)
 #endif
@@ -396,7 +400,7 @@ struct hb_no_trace_t {
 #define TRACE_APPLY(this) \
 	hb_auto_trace_t<HB_DEBUG_APPLY, bool> trace \
 	(&c->debug_depth, c->get_name (), this, HB_FUNC, \
-	 "idx %d gid %u lookup %d", \
+	 "idx %u gid %u lookup %d", \
 	 c->buffer->idx, c->buffer->cur().codepoint, (int) c->lookup_index)
 #else
 #define TRACE_APPLY(this) hb_no_trace_t<bool> trace
@@ -454,7 +458,7 @@ struct hb_no_trace_t {
 #define TRACE_DISPATCH(this, format) \
 	hb_auto_trace_t<context_t::max_debug_depth, typename context_t::return_t> trace \
 	(&c->debug_depth, c->get_name (), this, HB_FUNC, \
-	 "format %d", (int) format)
+	 "format %u", (unsigned) format)
 #else
 #define TRACE_DISPATCH(this, format) hb_no_trace_t<typename context_t::return_t> trace
 #endif

+ 2 - 1
thirdparty/harfbuzz/src/hb-deprecated.h

@@ -102,7 +102,8 @@ typedef hb_bool_t (*hb_font_get_glyph_func_t) (hb_font_t *font, void *font_data,
 					       hb_codepoint_t *glyph,
 					       void *user_data);
 
-HB_EXTERN HB_DEPRECATED_FOR(hb_font_funcs_set_nominal_glyph_func and hb_font_funcs_set_variation_glyph_func) void
+HB_DEPRECATED_FOR (hb_font_funcs_set_nominal_glyph_func and hb_font_funcs_set_variation_glyph_func)
+HB_EXTERN void
 hb_font_funcs_set_glyph_func (hb_font_funcs_t *ffuncs,
 			      hb_font_get_glyph_func_t func,
 			      void *user_data, hb_destroy_func_t destroy);

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

@@ -251,16 +251,12 @@ _hb_directwrite_shaper_face_data_destroy (hb_directwrite_face_data_t *data)
       data->dwriteFactory->UnregisterFontFileLoader (data->fontFileLoader);
     data->dwriteFactory->Release ();
   }
-  if (data->fontFileLoader)
-    delete data->fontFileLoader;
-  if (data->fontFileStream)
-    delete data->fontFileStream;
-  if (data->faceBlob)
-    hb_blob_destroy (data->faceBlob);
+  delete data->fontFileLoader;
+  delete data->fontFileStream;
+  hb_blob_destroy (data->faceBlob);
   if (data->dwrite_dll)
     FreeLibrary (data->dwrite_dll);
-  if (data)
-    delete data;
+  delete data;
 }
 
 

+ 63 - 2
thirdparty/harfbuzz/src/hb-draw.cc

@@ -35,6 +35,8 @@
  * @include: hb.h
  *
  * Functions for drawing (extracting) glyph shapes.
+ *
+ * The #hb_draw_funcs_t struct can be used with hb_font_draw_glyph().
  **/
 
 static void
@@ -198,13 +200,29 @@ DEFINE_NULL_INSTANCE (hb_draw_funcs_t) =
   }
 };
 
+/**
+ * hb_draw_funcs_get_empty:
+ *
+ * Fetches the singleton empty draw-functions structure.
+ *
+ * Return value: (transfer full): The empty draw-functions structure
+ *
+ * Since: 7.0.0
+ **/
+hb_draw_funcs_t *
+hb_draw_funcs_get_empty ()
+{
+  return const_cast<hb_draw_funcs_t *> (&Null (hb_draw_funcs_t));
+}
 
 /**
  * hb_draw_funcs_reference: (skip)
  * @dfuncs: draw functions
  *
- * Increases the reference count on @dfuncs by one. This prevents @buffer from
- * being destroyed until a matching call to hb_draw_funcs_destroy() is made.
+ * Increases the reference count on @dfuncs by one.
+ *
+ * This prevents @dfuncs from being destroyed until a matching
+ * call to hb_draw_funcs_destroy() is made.
  *
  * Return value: (transfer full):
  * The referenced #hb_draw_funcs_t.
@@ -246,6 +264,49 @@ hb_draw_funcs_destroy (hb_draw_funcs_t *dfuncs)
   hb_free (dfuncs);
 }
 
+/**
+ * hb_draw_funcs_set_user_data: (skip)
+ * @dfuncs: The draw-functions structure
+ * @key: The user-data key
+ * @data: A pointer to the user data
+ * @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. 
+ *
+ * Return value: `true` if success, `false` otherwise
+ *
+ * Since: 7.0.0
+ **/
+hb_bool_t
+hb_draw_funcs_set_user_data (hb_draw_funcs_t *dfuncs,
+			     hb_user_data_key_t *key,
+			     void *              data,
+			     hb_destroy_func_t   destroy,
+			     hb_bool_t           replace)
+{
+  return hb_object_set_user_data (dfuncs, key, data, destroy, replace);
+}
+
+/**
+ * hb_draw_funcs_get_user_data: (skip)
+ * @dfuncs: The draw-functions structure
+ * @key: The user-data key to query
+ *
+ * Fetches the user-data associated with the specified key,
+ * attached to the specified draw-functions structure.
+ *
+ * Return value: (transfer none): A pointer to the user data
+ *
+ * Since: 7.0.0
+ **/
+void *
+hb_draw_funcs_get_user_data (const hb_draw_funcs_t *dfuncs,
+			     hb_user_data_key_t       *key)
+{
+  return hb_object_get_user_data (dfuncs, key);
+}
+
 /**
  * hb_draw_funcs_make_immutable:
  * @dfuncs: draw functions

+ 25 - 10
thirdparty/harfbuzz/src/hb-draw.h

@@ -92,11 +92,11 @@ typedef struct hb_draw_funcs_t hb_draw_funcs_t;
 /**
  * hb_draw_move_to_func_t:
  * @dfuncs: draw functions object
- * @draw_data: The data accompanying the draw functions
+ * @draw_data: The data accompanying the draw functions in hb_font_draw_glyph()
  * @st: current draw state
  * @to_x: X component of target point
  * @to_y: Y component of target point
- * @user_data: User data pointer passed by the caller
+ * @user_data: User data pointer passed to hb_draw_funcs_set_move_to_func()
  *
  * A virtual method for the #hb_draw_funcs_t to perform a "move-to" draw
  * operation.
@@ -112,11 +112,11 @@ typedef void (*hb_draw_move_to_func_t) (hb_draw_funcs_t *dfuncs, void *draw_data
 /**
  * hb_draw_line_to_func_t:
  * @dfuncs: draw functions object
- * @draw_data: The data accompanying the draw functions
+ * @draw_data: The data accompanying the draw functions in hb_font_draw_glyph()
  * @st: current draw state
  * @to_x: X component of target point
  * @to_y: Y component of target point
- * @user_data: User data pointer passed by the caller
+ * @user_data: User data pointer passed to hb_draw_funcs_set_line_to_func()
  *
  * A virtual method for the #hb_draw_funcs_t to perform a "line-to" draw
  * operation.
@@ -132,13 +132,13 @@ typedef void (*hb_draw_line_to_func_t) (hb_draw_funcs_t *dfuncs, void *draw_data
 /**
  * hb_draw_quadratic_to_func_t:
  * @dfuncs: draw functions object
- * @draw_data: The data accompanying the draw functions
+ * @draw_data: The data accompanying the draw functions in hb_font_draw_glyph()
  * @st: current draw state
  * @control_x: X component of control point
  * @control_y: Y component of control point
  * @to_x: X component of target point
  * @to_y: Y component of target point
- * @user_data: User data pointer passed by the caller
+ * @user_data: User data pointer passed to hb_draw_funcs_set_quadratic_to_func()
  *
  * A virtual method for the #hb_draw_funcs_t to perform a "quadratic-to" draw
  * operation.
@@ -155,7 +155,7 @@ typedef void (*hb_draw_quadratic_to_func_t) (hb_draw_funcs_t *dfuncs, void *draw
 /**
  * hb_draw_cubic_to_func_t:
  * @dfuncs: draw functions object
- * @draw_data: The data accompanying the draw functions
+ * @draw_data: The data accompanying the draw functions in hb_font_draw_glyph()
  * @st: current draw state
  * @control1_x: X component of first control point
  * @control1_y: Y component of first control point
@@ -163,7 +163,7 @@ typedef void (*hb_draw_quadratic_to_func_t) (hb_draw_funcs_t *dfuncs, void *draw
  * @control2_y: Y component of second control point
  * @to_x: X component of target point
  * @to_y: Y component of target point
- * @user_data: User data pointer passed by the caller
+ * @user_data: User data pointer passed to hb_draw_funcs_set_cubic_to_func()
  *
  * A virtual method for the #hb_draw_funcs_t to perform a "cubic-to" draw
  * operation.
@@ -181,9 +181,9 @@ typedef void (*hb_draw_cubic_to_func_t) (hb_draw_funcs_t *dfuncs, void *draw_dat
 /**
  * hb_draw_close_path_func_t:
  * @dfuncs: draw functions object
- * @draw_data: The data accompanying the draw functions
+ * @draw_data: The data accompanying the draw functions in hb_font_draw_glyph()
  * @st: current draw state
- * @user_data: User data pointer passed by the caller
+ * @user_data: User data pointer passed to hb_draw_funcs_set_close_path_func()
  *
  * A virtual method for the #hb_draw_funcs_t to perform a "close-path" draw
  * operation.
@@ -279,12 +279,27 @@ hb_draw_funcs_set_close_path_func (hb_draw_funcs_t           *dfuncs,
 HB_EXTERN hb_draw_funcs_t *
 hb_draw_funcs_create (void);
 
+HB_EXTERN hb_draw_funcs_t *
+hb_draw_funcs_get_empty (void);
+
 HB_EXTERN hb_draw_funcs_t *
 hb_draw_funcs_reference (hb_draw_funcs_t *dfuncs);
 
 HB_EXTERN void
 hb_draw_funcs_destroy (hb_draw_funcs_t *dfuncs);
 
+HB_EXTERN hb_bool_t
+hb_draw_funcs_set_user_data (hb_draw_funcs_t *dfuncs,
+			     hb_user_data_key_t *key,
+			     void *              data,
+			     hb_destroy_func_t   destroy,
+			     hb_bool_t           replace);
+
+
+HB_EXTERN void *
+hb_draw_funcs_get_user_data (const hb_draw_funcs_t *dfuncs,
+			     hb_user_data_key_t       *key);
+
 HB_EXTERN void
 hb_draw_funcs_make_immutable (hb_draw_funcs_t *dfuncs);
 

+ 246 - 0
thirdparty/harfbuzz/src/hb-face-builder.cc

@@ -0,0 +1,246 @@
+/*
+ * Copyright © 2009  Red Hat, Inc.
+ * Copyright © 2012  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#include "hb.hh"
+
+#include "hb-face.hh"
+
+#include "hb-map.hh"
+#include "hb-open-file.hh"
+#include "hb-serialize.hh"
+
+
+/*
+ * face-builder: A face that has add_table().
+ */
+
+struct face_table_info_t
+{
+  hb_blob_t* data;
+  signed order;
+};
+
+struct hb_face_builder_data_t
+{
+  hb_hashmap_t<hb_tag_t, face_table_info_t> tables;
+};
+
+static int compare_entries (const void* pa, const void* pb)
+{
+  const auto& a = * (const hb_pair_t<hb_tag_t, face_table_info_t> *) pa;
+  const auto& b = * (const hb_pair_t<hb_tag_t, face_table_info_t> *) pb;
+
+  /* Order by blob size first (smallest to largest) and then table tag */
+
+  if (a.second.order != b.second.order)
+    return a.second.order < b.second.order ? -1 : +1;
+
+  if (a.second.data->length != b.second.data->length)
+    return a.second.data->length < b.second.data->length ? -1 : +1;
+
+  return a.first < b.first ? -1 : a.first == b.first ? 0 : +1;
+}
+
+static hb_face_builder_data_t *
+_hb_face_builder_data_create ()
+{
+  hb_face_builder_data_t *data = (hb_face_builder_data_t *) hb_calloc (1, sizeof (hb_face_builder_data_t));
+  if (unlikely (!data))
+    return nullptr;
+
+  data->tables.init ();
+
+  return data;
+}
+
+static void
+_hb_face_builder_data_destroy (void *user_data)
+{
+  hb_face_builder_data_t *data = (hb_face_builder_data_t *) user_data;
+
+  for (auto info : data->tables.values())
+    hb_blob_destroy (info.data);
+
+  data->tables.fini ();
+
+  hb_free (data);
+}
+
+static hb_blob_t *
+_hb_face_builder_data_reference_blob (hb_face_builder_data_t *data)
+{
+
+  unsigned int table_count = data->tables.get_population ();
+  unsigned int face_length = table_count * 16 + 12;
+
+  for (auto info : data->tables.values())
+    face_length += hb_ceil_to_4 (hb_blob_get_length (info.data));
+
+  char *buf = (char *) hb_malloc (face_length);
+  if (unlikely (!buf))
+    return nullptr;
+
+  hb_serialize_context_t c (buf, face_length);
+  c.propagate_error (data->tables);
+  OT::OpenTypeFontFile *f = c.start_serialize<OT::OpenTypeFontFile> ();
+
+  bool is_cff = (data->tables.has (HB_TAG ('C','F','F',' '))
+                 || data->tables.has (HB_TAG ('C','F','F','2')));
+  hb_tag_t sfnt_tag = is_cff ? OT::OpenTypeFontFile::CFFTag : OT::OpenTypeFontFile::TrueTypeTag;
+
+  // Sort the tags so that produced face is deterministic.
+  hb_vector_t<hb_pair_t <hb_tag_t, face_table_info_t>> sorted_entries;
+  data->tables.iter () | hb_sink (sorted_entries);
+  if (unlikely (sorted_entries.in_error ()))
+  {
+    hb_free (buf);
+    return nullptr;
+  }
+
+  sorted_entries.qsort (compare_entries);
+
+  bool ret = f->serialize_single (&c,
+                                  sfnt_tag,
+                                  + sorted_entries.iter()
+                                  | hb_map ([&] (hb_pair_t<hb_tag_t, face_table_info_t> _) {
+                                    return hb_pair_t<hb_tag_t, hb_blob_t*> (_.first, _.second.data);
+                                  }));
+
+  c.end_serialize ();
+
+  if (unlikely (!ret))
+  {
+    hb_free (buf);
+    return nullptr;
+  }
+
+  return hb_blob_create (buf, face_length, HB_MEMORY_MODE_WRITABLE, buf, hb_free);
+}
+
+static hb_blob_t *
+_hb_face_builder_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data)
+{
+  hb_face_builder_data_t *data = (hb_face_builder_data_t *) user_data;
+
+  if (!tag)
+    return _hb_face_builder_data_reference_blob (data);
+
+  return hb_blob_reference (data->tables[tag].data);
+}
+
+
+/**
+ * hb_face_builder_create:
+ *
+ * Creates a #hb_face_t that can be used with hb_face_builder_add_table().
+ * After tables are added to the face, it can be compiled to a binary
+ * font file by calling hb_face_reference_blob().
+ *
+ * Return value: (transfer full): New face.
+ *
+ * Since: 1.9.0
+ **/
+hb_face_t *
+hb_face_builder_create ()
+{
+  hb_face_builder_data_t *data = _hb_face_builder_data_create ();
+  if (unlikely (!data)) return hb_face_get_empty ();
+
+  return hb_face_create_for_tables (_hb_face_builder_reference_table,
+				    data,
+				    _hb_face_builder_data_destroy);
+}
+
+/**
+ * hb_face_builder_add_table:
+ * @face: A face object created with hb_face_builder_create()
+ * @tag: The #hb_tag_t of the table to add
+ * @blob: The blob containing the table data to add
+ *
+ * Add table for @tag with data provided by @blob to the face.  @face must
+ * be created using hb_face_builder_create().
+ *
+ * Since: 1.9.0
+ **/
+hb_bool_t
+hb_face_builder_add_table (hb_face_t *face, hb_tag_t tag, hb_blob_t *blob)
+{
+  if (unlikely (face->destroy != (hb_destroy_func_t) _hb_face_builder_data_destroy))
+    return false;
+
+  if (tag == HB_MAP_VALUE_INVALID)
+    return false;
+
+  hb_face_builder_data_t *data = (hb_face_builder_data_t *) face->user_data;
+
+  hb_blob_t* previous = data->tables.get (tag).data;
+  if (!data->tables.set (tag, face_table_info_t {hb_blob_reference (blob), -1}))
+  {
+    hb_blob_destroy (blob);
+    return false;
+  }
+
+  hb_blob_destroy (previous);
+  return true;
+}
+
+/**
+ * hb_face_builder_sort_tables:
+ * @face: A face object created with hb_face_builder_create()
+ * @tags: (array zero-terminated=1): ordered list of table tags terminated by
+ *   %HB_TAG_NONE
+ *
+ * Set the ordering of tables for serialization. Any tables not
+ * specified in the tags list will be ordered after the tables in
+ * tags, ordered by the default sort ordering.
+ *
+ * Since: 5.3.0
+ **/
+void
+hb_face_builder_sort_tables (hb_face_t *face,
+                             const hb_tag_t  *tags)
+{
+  if (unlikely (face->destroy != (hb_destroy_func_t) _hb_face_builder_data_destroy))
+    return;
+
+  hb_face_builder_data_t *data = (hb_face_builder_data_t *) face->user_data;
+
+  // Sort all unspecified tables after any specified tables.
+  for (auto& info : data->tables.values_ref())
+    info.order = (unsigned) -1;
+
+  signed order = 0;
+  for (const hb_tag_t* tag = tags;
+       *tag;
+       tag++)
+  {
+    face_table_info_t* info;
+    if (!data->tables.has (*tag, &info)) continue;
+    info->order = order++;
+  }
+}

+ 32 - 216
thirdparty/harfbuzz/src/hb-face.cc

@@ -33,7 +33,6 @@
 #include "hb-open-file.hh"
 #include "hb-ot-face.hh"
 #include "hb-ot-cmap-table.hh"
-#include "hb-map.hh"
 
 
 /**
@@ -472,6 +471,8 @@ hb_face_get_index (const hb_face_t *face)
  *
  * Sets the units-per-em (upem) for a face object to the specified value.
  *
+ * This API is used in rare circumstances.
+ *
  * Since: 0.9.2
  **/
 void
@@ -488,7 +489,10 @@ hb_face_set_upem (hb_face_t    *face,
  * hb_face_get_upem:
  * @face: A face object
  *
- * Fetches the units-per-em (upem) value of the specified face object.
+ * Fetches the units-per-em (UPEM) value of the specified face object.
+ *
+ * Typical UPEM values for fonts are 1000, or 2048, but any value
+ * in between 16 and 16,384 is allowed for OpenType fonts.
  *
  * Return value: The upem value of @face
  *
@@ -507,6 +511,8 @@ hb_face_get_upem (const hb_face_t *face)
  *
  * Sets the glyph count for a face object to the specified value.
  *
+ * This API is used in rare circumstances.
+ *
  * Since: 0.9.7
  **/
 void
@@ -581,7 +587,7 @@ hb_face_get_table_tags (const hb_face_t *face,
 /**
  * hb_face_collect_unicodes:
  * @face: A face object
- * @out: The set to add Unicode characters to
+ * @out: (out): The set to add Unicode characters to
  *
  * Collects all of the Unicode characters covered by @face and adds
  * them to the #hb_set_t set @out.
@@ -594,10 +600,31 @@ hb_face_collect_unicodes (hb_face_t *face,
 {
   face->table.cmap->collect_unicodes (out, face->get_num_glyphs ());
 }
+/**
+ * hb_face_collect_nominal_glyph_mapping:
+ * @face: A face object
+ * @mapping: (out): The map to add Unicode-to-glyph mapping to
+ * @unicodes: (nullable) (out): The set to add Unicode characters to, or `NULL`
+ *
+ * Collects the mapping from Unicode characters to nominal glyphs of the @face,
+ * and optionally all of the Unicode characters covered by @face.
+ *
+ * Since: 7.0.0
+ */
+void
+hb_face_collect_nominal_glyph_mapping (hb_face_t *face,
+				       hb_map_t  *mapping,
+				       hb_set_t  *unicodes)
+{
+  hb_set_t stack_unicodes;
+  if (!unicodes)
+    unicodes = &stack_unicodes;
+  face->table.cmap->collect_mapping (unicodes, mapping, face->get_num_glyphs ());
+}
 /**
  * hb_face_collect_variation_selectors:
  * @face: A face object
- * @out: The set to add Variation Selector characters to
+ * @out: (out): The set to add Variation Selector characters to
  *
  * Collects all Unicode "Variation Selector" characters covered by @face and adds
  * them to the #hb_set_t set @out.
@@ -614,7 +641,7 @@ hb_face_collect_variation_selectors (hb_face_t *face,
  * hb_face_collect_variation_unicodes:
  * @face: A face object
  * @variation_selector: The Variation Selector to query
- * @out: The set to add Unicode characters to
+ * @out: (out): The set to add Unicode characters to
  *
  * Collects all Unicode characters for @variation_selector covered by @face and adds
  * them to the #hb_set_t set @out.
@@ -629,214 +656,3 @@ hb_face_collect_variation_unicodes (hb_face_t *face,
   face->table.cmap->collect_variation_unicodes (variation_selector, out);
 }
 #endif
-
-
-/*
- * face-builder: A face that has add_table().
- */
-
-struct face_table_info_t
-{
-  hb_blob_t* data;
-  signed order;
-};
-
-struct hb_face_builder_data_t
-{
-  hb_hashmap_t<hb_tag_t, face_table_info_t> tables;
-};
-
-static int compare_entries (const void* pa, const void* pb)
-{
-  const auto& a = * (const hb_pair_t<hb_tag_t, face_table_info_t> *) pa;
-  const auto& b = * (const hb_pair_t<hb_tag_t, face_table_info_t> *) pb;
-
-  /* Order by blob size first (smallest to largest) and then table tag */
-
-  if (a.second.order != b.second.order)
-    return a.second.order < b.second.order ? -1 : +1;
-
-  if (a.second.data->length != b.second.data->length)
-    return a.second.data->length < b.second.data->length ? -1 : +1;
-
-  return a.first < b.first ? -1 : a.first == b.first ? 0 : +1;
-}
-
-static hb_face_builder_data_t *
-_hb_face_builder_data_create ()
-{
-  hb_face_builder_data_t *data = (hb_face_builder_data_t *) hb_calloc (1, sizeof (hb_face_builder_data_t));
-  if (unlikely (!data))
-    return nullptr;
-
-  data->tables.init ();
-
-  return data;
-}
-
-static void
-_hb_face_builder_data_destroy (void *user_data)
-{
-  hb_face_builder_data_t *data = (hb_face_builder_data_t *) user_data;
-
-  for (auto info : data->tables.values())
-    hb_blob_destroy (info.data);
-
-  data->tables.fini ();
-
-  hb_free (data);
-}
-
-static hb_blob_t *
-_hb_face_builder_data_reference_blob (hb_face_builder_data_t *data)
-{
-
-  unsigned int table_count = data->tables.get_population ();
-  unsigned int face_length = table_count * 16 + 12;
-
-  for (auto info : data->tables.values())
-    face_length += hb_ceil_to_4 (hb_blob_get_length (info.data));
-
-  char *buf = (char *) hb_malloc (face_length);
-  if (unlikely (!buf))
-    return nullptr;
-
-  hb_serialize_context_t c (buf, face_length);
-  c.propagate_error (data->tables);
-  OT::OpenTypeFontFile *f = c.start_serialize<OT::OpenTypeFontFile> ();
-
-  bool is_cff = (data->tables.has (HB_TAG ('C','F','F',' '))
-                 || data->tables.has (HB_TAG ('C','F','F','2')));
-  hb_tag_t sfnt_tag = is_cff ? OT::OpenTypeFontFile::CFFTag : OT::OpenTypeFontFile::TrueTypeTag;
-
-  // Sort the tags so that produced face is deterministic.
-  hb_vector_t<hb_pair_t <hb_tag_t, face_table_info_t>> sorted_entries;
-  data->tables.iter () | hb_sink (sorted_entries);
-  if (unlikely (sorted_entries.in_error ()))
-  {
-    hb_free (buf);
-    return nullptr;
-  }
-
-  sorted_entries.qsort (compare_entries);
-
-  bool ret = f->serialize_single (&c,
-                                  sfnt_tag,
-                                  + sorted_entries.iter()
-                                  | hb_map ([&] (hb_pair_t<hb_tag_t, face_table_info_t> _) {
-                                    return hb_pair_t<hb_tag_t, hb_blob_t*> (_.first, _.second.data);
-                                  }));
-
-  c.end_serialize ();
-
-  if (unlikely (!ret))
-  {
-    hb_free (buf);
-    return nullptr;
-  }
-
-  return hb_blob_create (buf, face_length, HB_MEMORY_MODE_WRITABLE, buf, hb_free);
-}
-
-static hb_blob_t *
-_hb_face_builder_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data)
-{
-  hb_face_builder_data_t *data = (hb_face_builder_data_t *) user_data;
-
-  if (!tag)
-    return _hb_face_builder_data_reference_blob (data);
-
-  return hb_blob_reference (data->tables[tag].data);
-}
-
-
-/**
- * hb_face_builder_create:
- *
- * Creates a #hb_face_t that can be used with hb_face_builder_add_table().
- * After tables are added to the face, it can be compiled to a binary
- * font file by calling hb_face_reference_blob().
- *
- * Return value: (transfer full): New face.
- *
- * Since: 1.9.0
- **/
-hb_face_t *
-hb_face_builder_create ()
-{
-  hb_face_builder_data_t *data = _hb_face_builder_data_create ();
-  if (unlikely (!data)) return hb_face_get_empty ();
-
-  return hb_face_create_for_tables (_hb_face_builder_reference_table,
-				    data,
-				    _hb_face_builder_data_destroy);
-}
-
-/**
- * hb_face_builder_add_table:
- * @face: A face object created with hb_face_builder_create()
- * @tag: The #hb_tag_t of the table to add
- * @blob: The blob containing the table data to add
- *
- * Add table for @tag with data provided by @blob to the face.  @face must
- * be created using hb_face_builder_create().
- *
- * Since: 1.9.0
- **/
-hb_bool_t
-hb_face_builder_add_table (hb_face_t *face, hb_tag_t tag, hb_blob_t *blob)
-{
-  if (unlikely (face->destroy != (hb_destroy_func_t) _hb_face_builder_data_destroy))
-    return false;
-
-  if (tag == HB_MAP_VALUE_INVALID)
-    return false;
-
-  hb_face_builder_data_t *data = (hb_face_builder_data_t *) face->user_data;
-
-  hb_blob_t* previous = data->tables.get (tag).data;
-  if (!data->tables.set (tag, face_table_info_t {hb_blob_reference (blob), -1}))
-  {
-    hb_blob_destroy (blob);
-    return false;
-  }
-
-  hb_blob_destroy (previous);
-  return true;
-}
-
-/**
- * hb_face_builder_sort_tables:
- * @face: A face object created with hb_face_builder_create()
- * @tags: (array zero-terminated=1): ordered list of table tags terminated by
- *   %HB_TAG_NONE
- *
- * Set the ordering of tables for serialization. Any tables not
- * specified in the tags list will be ordered after the tables in
- * tags, ordered by the default sort ordering.
- *
- * Since: 5.3.0
- **/
-void
-hb_face_builder_sort_tables (hb_face_t *face,
-                             const hb_tag_t  *tags)
-{
-  if (unlikely (face->destroy != (hb_destroy_func_t) _hb_face_builder_data_destroy))
-    return;
-
-  hb_face_builder_data_t *data = (hb_face_builder_data_t *) face->user_data;
-
-  // Sort all unspecified tables after any specified tables.
-  for (auto& info : data->tables.values_ref())
-    info.order = (unsigned) -1;
-
-  signed order = 0;
-  for (const hb_tag_t* tag = tags;
-       *tag;
-       tag++)
-  {
-    face_table_info_t* info;
-    if (!data->tables.has (*tag, &info)) continue;
-    info->order = order++;
-  }
-}

+ 6 - 0
thirdparty/harfbuzz/src/hb-face.h

@@ -33,6 +33,7 @@
 
 #include "hb-common.h"
 #include "hb-blob.h"
+#include "hb-map.h"
 #include "hb-set.h"
 
 HB_BEGIN_DECLS
@@ -149,6 +150,11 @@ HB_EXTERN void
 hb_face_collect_unicodes (hb_face_t *face,
 			  hb_set_t  *out);
 
+HB_EXTERN void
+hb_face_collect_nominal_glyph_mapping (hb_face_t *face,
+				       hb_map_t  *mapping,
+				       hb_set_t  *unicodes);
+
 HB_EXTERN void
 hb_face_collect_variation_selectors (hb_face_t *face,
 				     hb_set_t  *out);

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است