[amalgamation] Add HB_HAS_SUBSET and share SVG helpers

Add HB_HAS_SUBSET to hb-features.h and wire it through the same
HAVE_* feature plumbing used by the other HB_HAS_* macros.

Refactor the raster/vector SVG helper code so the duplicated
private helpers live in shared internal headers, avoiding
duplicate definitions when sources are combined in a single
translation unit.

Testing: meson compile -C build

Assisted-by: OpenAI Codex
diff --git a/docs/harfbuzz-sections.txt b/docs/harfbuzz-sections.txt
index bee7ef0..5506375 100644
--- a/docs/harfbuzz-sections.txt
+++ b/docs/harfbuzz-sections.txt
@@ -207,6 +207,7 @@
 HB_HAS_GRAPHITE
 HB_HAS_ICU
 HB_HAS_RASTER
+HB_HAS_SUBSET
 HB_HAS_UNISCRIBE
 HB_HAS_VECTOR
 HB_HAS_WASM
diff --git a/meson.build b/meson.build
index c6af11f..3029675 100644
--- a/meson.build
+++ b/meson.build
@@ -269,6 +269,10 @@
   conf.set('HAVE_VECTOR', 1)
 endif
 
+if not get_option('subset').disabled()
+  conf.set('HAVE_SUBSET', 1)
+endif
+
 if freetype_dep.found()
   conf.set('HAVE_FREETYPE', 1)
   check_freetype_funcs = [
diff --git a/src/hb-features.h.in b/src/hb-features.h.in
index 9726140..99bc43f 100644
--- a/src/hb-features.h.in
+++ b/src/hb-features.h.in
@@ -107,6 +107,13 @@
 #mesondefine HB_HAS_RASTER
 
 /**
+ * HB_HAS_SUBSET:
+ *
+ * Defined if Harfbuzz has been built with the subset library.
+ */
+#mesondefine HB_HAS_SUBSET
+
+/**
  * HB_HAS_UNISCRIBE:
  *
  * Defined if Harfbuzz has been built with Uniscribe support.
diff --git a/src/hb-raster-svg-clip.cc b/src/hb-raster-svg-clip.cc
index edae750..19714ea 100644
--- a/src/hb-raster-svg-clip.cc
+++ b/src/hb-raster-svg-clip.cc
@@ -89,19 +89,6 @@
     *ctx->had_alloc_failure = true;
 }
 
-static void
-svg_skip_subtree (hb_svg_xml_parser_t &parser)
-{
-  int depth = 1;
-  while (depth > 0)
-  {
-    hb_svg_token_type_t tok = parser.next ();
-    if (tok == SVG_TOKEN_EOF) break;
-    if (tok == SVG_TOKEN_CLOSE_TAG) depth--;
-    else if (tok == SVG_TOKEN_OPEN_TAG) depth++;
-  }
-}
-
 static inline bool
 svg_resolve_element_visibility (hb_svg_xml_parser_t &parser,
                                 bool parent_visible)
diff --git a/src/hb-raster-svg-parse.hh b/src/hb-raster-svg-parse.hh
index b8fae86..21c782c 100644
--- a/src/hb-raster-svg-parse.hh
+++ b/src/hb-raster-svg-parse.hh
@@ -202,6 +202,19 @@
   }
 };
 
+static inline void
+svg_skip_subtree (hb_svg_xml_parser_t &parser)
+{
+  int depth = 1;
+  while (depth > 0)
+  {
+    hb_svg_token_type_t tok = parser.next ();
+    if (tok == SVG_TOKEN_EOF) break;
+    if (tok == SVG_TOKEN_CLOSE_TAG) depth--;
+    else if (tok == SVG_TOKEN_OPEN_TAG) depth++;
+  }
+}
+
 static inline hb_svg_str_t
 svg_pick_attr_or_style (const hb_svg_xml_parser_t &parser,
                         hb_svg_str_t style_value,
diff --git a/src/hb-raster-svg-render.cc b/src/hb-raster-svg-render.cc
index e39b0d9..d07ce04 100644
--- a/src/hb-raster-svg-render.cc
+++ b/src/hb-raster-svg-render.cc
@@ -306,19 +306,6 @@
   ctx->allow_symbol_render_once = old_allow_symbol;
 }
 
-static void
-svg_skip_subtree (hb_svg_xml_parser_t &parser)
-{
-  int depth = 1;
-  while (depth > 0)
-  {
-    hb_svg_token_type_t tok = parser.next ();
-    if (tok == SVG_TOKEN_EOF) break;
-    if (tok == SVG_TOKEN_CLOSE_TAG) depth--;
-    else if (tok == SVG_TOKEN_OPEN_TAG) depth++;
-  }
-}
-
 /* Render one element (may be a container or shape) */
 static void
 svg_render_element (hb_svg_render_context_t *ctx,
diff --git a/src/hb-vector-svg-draw.cc b/src/hb-vector-svg-draw.cc
index dd80f74..82f31b4 100644
--- a/src/hb-vector-svg-draw.cc
+++ b/src/hb-vector-svg-draw.cc
@@ -39,81 +39,7 @@
 #include <math.h>
 #include <string.h>
 
-
-static bool
-hb_svg_append_str (hb_vector_t<char> *buf, const char *s)
-{
-  return hb_svg_append_len (buf, s, (unsigned) strlen (s));
-}
-
-static bool
-hb_svg_append_unsigned (hb_vector_t<char> *buf, unsigned v)
-{
-  char tmp[10];
-  unsigned n = 0;
-  do {
-    tmp[n++] = (char) ('0' + (v % 10));
-    v /= 10;
-  } while (v);
-
-  unsigned old_len = buf->length;
-  if (unlikely (!buf->resize_dirty ((int) (old_len + n))))
-    return false;
-
-  for (unsigned i = 0; i < n; i++)
-    buf->arrayZ[old_len + i] = tmp[n - 1 - i];
-  return true;
-}
-
-static bool
-hb_svg_append_hex_byte (hb_vector_t<char> *buf, unsigned v)
-{
-  static const char hex[] = "0123456789ABCDEF";
-  char tmp[2] = {hex[(v >> 4) & 15], hex[v & 15]};
-  return hb_svg_append_len (buf, tmp, 2);
-}
-
-HB_UNUSED static bool
-hb_svg_append_base64 (hb_vector_t<char> *buf,
-                      const uint8_t *data,
-                      unsigned len)
-{
-  static const char b64[] =
-    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-
-  unsigned out_len = ((len + 2) / 3) * 4;
-  unsigned old_len = buf->length;
-  if (unlikely (!buf->resize_dirty ((int) (old_len + out_len))))
-    return false;
-
-  char *dst = buf->arrayZ + old_len;
-  unsigned di = 0;
-  unsigned i = 0;
-  while (i + 2 < len)
-  {
-    unsigned v = ((unsigned) data[i] << 16) |
-                 ((unsigned) data[i + 1] << 8) |
-                 ((unsigned) data[i + 2]);
-    dst[di++] = b64[(v >> 18) & 63];
-    dst[di++] = b64[(v >> 12) & 63];
-    dst[di++] = b64[(v >> 6) & 63];
-    dst[di++] = b64[v & 63];
-    i += 3;
-  }
-
-  if (i < len)
-  {
-    unsigned v = (unsigned) data[i] << 16;
-    if (i + 1 < len)
-      v |= (unsigned) data[i + 1] << 8;
-    dst[di++] = b64[(v >> 18) & 63];
-    dst[di++] = b64[(v >> 12) & 63];
-    dst[di++] = (i + 1 < len) ? b64[(v >> 6) & 63] : '=';
-    dst[di++] = '=';
-  }
-
-  return true;
-}
+#include "hb-vector-svg.hh"
 
 HB_UNUSED static inline bool
 hb_svg_buffer_contains (const hb_vector_t<char> &buf, const char *needle)
@@ -129,239 +55,6 @@
   return false;
 }
 
-struct hb_svg_blob_meta_t
-{
-  char *data;
-  int allocated;
-  bool transferred;
-  bool in_replace;
-};
-
-static hb_user_data_key_t hb_svg_blob_meta_user_data_key;
-
-static void
-hb_svg_blob_meta_set_buffer (hb_svg_blob_meta_t *meta,
-			     char *data,
-			     int allocated)
-{
-  meta->data = data;
-  meta->allocated = allocated;
-  meta->transferred = false;
-}
-
-static void
-hb_svg_blob_meta_release_buffer (hb_svg_blob_meta_t *meta)
-{
-  if (!meta)
-    return;
-  if (!meta->transferred && meta->data)
-    hb_free (meta->data);
-  meta->data = nullptr;
-  meta->allocated = 0;
-  meta->transferred = true;
-}
-
-static void
-hb_svg_blob_meta_destroy (void *data)
-{
-  auto *meta = (hb_svg_blob_meta_t *) data;
-  hb_svg_blob_meta_release_buffer (meta);
-  if (meta->in_replace)
-  {
-    meta->in_replace = false;
-    return;
-  }
-  hb_free (meta);
-}
-
-static hb_blob_t *
-hb_svg_blob_from_buffer (hb_blob_t **recycled_blob,
-			 hb_vector_t<char> *buf)
-{
-  unsigned len = 0;
-  int allocated = 0;
-  char *data = buf->steal (&len, &allocated);
-  if (!data)
-    return nullptr;
-
-  hb_blob_t *blob = nullptr;
-  if (*recycled_blob)
-    blob = *recycled_blob;
-  bool reused_blob = blob && blob != hb_blob_get_empty ();
-  bool new_meta = false;
-  auto *meta = reused_blob
-             ? (hb_svg_blob_meta_t *) hb_blob_get_user_data (blob, &hb_svg_blob_meta_user_data_key)
-             : nullptr;
-  if (!meta)
-  {
-    meta = (hb_svg_blob_meta_t *) hb_malloc (sizeof (hb_svg_blob_meta_t));
-    if (!meta)
-    {
-      hb_free (data);
-      return nullptr;
-    }
-    meta->data = nullptr;
-    meta->allocated = 0;
-    meta->transferred = true;
-    meta->in_replace = false;
-    new_meta = true;
-  }
-
-  if (reused_blob)
-  {
-    /* replace_buffer() first destroys old buffer user_data; keep meta alive. */
-    meta->in_replace = true;
-    blob->replace_buffer (data, len, HB_MEMORY_MODE_WRITABLE, meta, hb_svg_blob_meta_destroy);
-    hb_svg_blob_meta_set_buffer (meta, data, allocated);
-  }
-  else
-  {
-    hb_svg_blob_meta_set_buffer (meta, data, allocated);
-    blob = hb_blob_create_or_fail (data, len, HB_MEMORY_MODE_WRITABLE, meta, hb_svg_blob_meta_destroy);
-    if (unlikely (!blob))
-      return nullptr;
-  }
-
-  if (unlikely (blob == hb_blob_get_empty ()))
-  {
-    if (new_meta)
-      hb_free (meta);
-    hb_free (data);
-    return nullptr;
-  }
-
-  if (new_meta &&
-      !hb_blob_set_user_data (blob,
-			      &hb_svg_blob_meta_user_data_key,
-			      meta,
-			      nullptr,
-			      true))
-  {
-    if (!reused_blob)
-      hb_blob_destroy (blob);
-    return nullptr;
-  }
-
-  if (*recycled_blob)
-    *recycled_blob = nullptr;
-
-  return blob;
-}
-
-static void
-hb_svg_recover_recycled_buffer (hb_blob_t *blob,
-				hb_vector_t<char> *buf)
-{
-  if (!blob)
-    return;
-
-  auto *meta = (hb_svg_blob_meta_t *) hb_blob_get_user_data (blob, &hb_svg_blob_meta_user_data_key);
-  if (!meta || meta->transferred || !meta->data)
-    return;
-
-  buf->recycle_buffer (meta->data, 0, meta->allocated);
-  meta->data = nullptr;
-  meta->allocated = 0;
-  meta->transferred = true;
-}
-
-HB_UNUSED static void
-hb_svg_append_color (hb_vector_t<char> *buf,
-                     hb_color_t color,
-                     bool with_alpha)
-{
-  static const char hex[] = "0123456789ABCDEF";
-  unsigned r = hb_color_get_red (color);
-  unsigned g = hb_color_get_green (color);
-  unsigned b = hb_color_get_blue (color);
-  unsigned a = hb_color_get_alpha (color);
-  hb_svg_append_c (buf, '#');
-  if (((r >> 4) == (r & 0xF)) &&
-      ((g >> 4) == (g & 0xF)) &&
-      ((b >> 4) == (b & 0xF)))
-  {
-    hb_svg_append_c (buf, hex[r & 0xF]);
-    hb_svg_append_c (buf, hex[g & 0xF]);
-    hb_svg_append_c (buf, hex[b & 0xF]);
-  }
-  else
-  {
-    hb_svg_append_hex_byte (buf, r);
-    hb_svg_append_hex_byte (buf, g);
-    hb_svg_append_hex_byte (buf, b);
-  }
-  if (with_alpha && a != 255)
-  {
-    hb_svg_append_str (buf, "\" fill-opacity=\"");
-    hb_svg_append_num (buf, a / 255.f, 4);
-  }
-}
-
-static void
-hb_svg_transform_point (const hb_transform_t<> &t,
-                        float x_scale_factor,
-                        float y_scale_factor,
-                        float x, float y,
-                        float *tx, float *ty)
-{
-  float xx = x, yy = y;
-  t.transform_point (xx, yy);
-  *tx = xx / (x_scale_factor > 0 ? x_scale_factor : 1.f);
-  *ty = yy / (y_scale_factor > 0 ? y_scale_factor : 1.f);
-}
-
-static hb_bool_t
-hb_svg_set_glyph_extents_common (const hb_transform_t<> &transform,
-                                 float x_scale_factor,
-                                 float y_scale_factor,
-                                 const hb_glyph_extents_t *glyph_extents,
-                                 hb_vector_extents_t *extents,
-                                 hb_bool_t *has_extents)
-{
-  float x0 = (float) glyph_extents->x_bearing;
-  float y0 = (float) glyph_extents->y_bearing;
-  float x1 = x0 + glyph_extents->width;
-  float y1 = y0 + glyph_extents->height;
-
-  float px[4] = {x0, x0, x1, x1};
-  float py[4] = {y0, y1, y0, y1};
-
-  float tx, ty;
-  hb_svg_transform_point (transform, x_scale_factor, y_scale_factor, px[0], py[0], &tx, &ty);
-  float tx_min = tx, tx_max = tx;
-  float ty_min = ty, ty_max = ty;
-
-  for (unsigned i = 1; i < 4; i++)
-  {
-    hb_svg_transform_point (transform, x_scale_factor, y_scale_factor, px[i], py[i], &tx, &ty);
-    tx_min = hb_min (tx_min, tx);
-    tx_max = hb_max (tx_max, tx);
-    ty_min = hb_min (ty_min, ty);
-    ty_max = hb_max (ty_max, ty);
-  }
-
-  if (tx_max <= tx_min || ty_max <= ty_min)
-  {
-    return false;
-  }
-
-  if (*has_extents)
-  {
-    float x0 = hb_min (extents->x, tx_min);
-    float y0 = hb_min (extents->y, ty_min);
-    float x1 = hb_max (extents->x + extents->width, tx_max);
-    float y1 = hb_max (extents->y + extents->height, ty_max);
-    *extents = {x0, y0, x1 - x0, y1 - y0};
-  }
-  else
-  {
-    *extents = {tx_min, ty_min, tx_max - tx_min, ty_max - ty_min};
-    *has_extents = true;
-  }
-  return true;
-}
-
-
 struct hb_vector_draw_t
 {
   hb_object_header_t header;
@@ -391,63 +84,6 @@
   }
 };
 
-static inline void
-hb_svg_append_instance_transform (hb_vector_t<char> *out,
-                                  unsigned precision,
-                                  float x_scale_factor,
-                                  float y_scale_factor,
-                                  float xx, float yx,
-                                  float xy, float yy,
-                                  float tx, float ty)
-{
-  unsigned sprec = hb_svg_scale_precision (precision);
-  if (xx == 1.f && yx == 0.f && xy == 0.f && yy == 1.f)
-  {
-    float sx = 1.f / x_scale_factor;
-    float sy = 1.f / y_scale_factor;
-    hb_svg_append_str (out, "translate(");
-    hb_svg_append_num (out, tx / x_scale_factor, precision);
-    hb_svg_append_c (out, ',');
-    hb_svg_append_num (out, -ty / y_scale_factor, precision);
-    hb_svg_append_str (out, ") scale(");
-    hb_svg_append_num (out, sx, sprec, true);
-    hb_svg_append_c (out, ',');
-    hb_svg_append_num (out, -sy, sprec, true);
-    hb_svg_append_c (out, ')');
-  }
-  else
-  {
-    hb_svg_append_str (out, "matrix(");
-    hb_svg_append_num (out, xx / x_scale_factor, sprec, true);
-    hb_svg_append_c (out, ',');
-    hb_svg_append_num (out, yx / y_scale_factor, sprec, true);
-    hb_svg_append_c (out, ',');
-    hb_svg_append_num (out, -xy / x_scale_factor, sprec, true);
-    hb_svg_append_c (out, ',');
-    hb_svg_append_num (out, -yy / y_scale_factor, sprec, true);
-    hb_svg_append_c (out, ',');
-    hb_svg_append_num (out, tx / x_scale_factor, precision);
-    hb_svg_append_c (out, ',');
-    hb_svg_append_num (out, -ty / y_scale_factor, precision);
-    hb_svg_append_c (out, ')');
-  }
-}
-
-HB_UNUSED static inline void
-hb_svg_append_image_instance_translate (hb_vector_t<char> *out,
-                                        unsigned precision,
-                                        float x_scale_factor,
-                                        float y_scale_factor,
-                                        float tx, float ty)
-{
-  hb_svg_append_str (out, "translate(");
-  hb_svg_append_num (out, tx / x_scale_factor, precision);
-  hb_svg_append_c (out, ',');
-  hb_svg_append_num (out, -ty / y_scale_factor, precision);
-  hb_svg_append_c (out, ')');
-}
-
-
 static void
 hb_vector_draw_move_to (hb_draw_funcs_t *,
                         void *draw_data,
diff --git a/src/hb-vector-svg-paint.cc b/src/hb-vector-svg-paint.cc
index 31e61bc..b23efb7 100644
--- a/src/hb-vector-svg-paint.cc
+++ b/src/hb-vector-svg-paint.cc
@@ -39,314 +39,7 @@
 #include <math.h>
 #include <string.h>
 
-
-static bool
-hb_svg_append_str (hb_vector_t<char> *buf, const char *s)
-{
-  return hb_svg_append_len (buf, s, (unsigned) strlen (s));
-}
-
-static bool
-hb_svg_append_unsigned (hb_vector_t<char> *buf, unsigned v)
-{
-  char tmp[10];
-  unsigned n = 0;
-  do {
-    tmp[n++] = (char) ('0' + (v % 10));
-    v /= 10;
-  } while (v);
-
-  unsigned old_len = buf->length;
-  if (unlikely (!buf->resize_dirty ((int) (old_len + n))))
-    return false;
-
-  for (unsigned i = 0; i < n; i++)
-    buf->arrayZ[old_len + i] = tmp[n - 1 - i];
-  return true;
-}
-
-static bool
-hb_svg_append_hex_byte (hb_vector_t<char> *buf, unsigned v)
-{
-  static const char hex[] = "0123456789ABCDEF";
-  char tmp[2] = {hex[(v >> 4) & 15], hex[v & 15]};
-  return hb_svg_append_len (buf, tmp, 2);
-}
-
-static bool
-hb_svg_append_base64 (hb_vector_t<char> *buf,
-                      const uint8_t *data,
-                      unsigned len)
-{
-  static const char b64[] =
-    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-
-  unsigned out_len = ((len + 2) / 3) * 4;
-  unsigned old_len = buf->length;
-  if (unlikely (!buf->resize_dirty ((int) (old_len + out_len))))
-    return false;
-
-  char *dst = buf->arrayZ + old_len;
-  unsigned di = 0;
-  unsigned i = 0;
-  while (i + 2 < len)
-  {
-    unsigned v = ((unsigned) data[i] << 16) |
-                 ((unsigned) data[i + 1] << 8) |
-                 ((unsigned) data[i + 2]);
-    dst[di++] = b64[(v >> 18) & 63];
-    dst[di++] = b64[(v >> 12) & 63];
-    dst[di++] = b64[(v >> 6) & 63];
-    dst[di++] = b64[v & 63];
-    i += 3;
-  }
-
-  if (i < len)
-  {
-    unsigned v = (unsigned) data[i] << 16;
-    if (i + 1 < len)
-      v |= (unsigned) data[i + 1] << 8;
-    dst[di++] = b64[(v >> 18) & 63];
-    dst[di++] = b64[(v >> 12) & 63];
-    dst[di++] = (i + 1 < len) ? b64[(v >> 6) & 63] : '=';
-    dst[di++] = '=';
-  }
-
-  return true;
-}
-
-struct hb_svg_blob_meta_t
-{
-  char *data;
-  int allocated;
-  bool transferred;
-  bool in_replace;
-};
-
-static hb_user_data_key_t hb_svg_blob_meta_user_data_key;
-
-static void
-hb_svg_blob_meta_set_buffer (hb_svg_blob_meta_t *meta,
-			     char *data,
-			     int allocated)
-{
-  meta->data = data;
-  meta->allocated = allocated;
-  meta->transferred = false;
-}
-
-static void
-hb_svg_blob_meta_release_buffer (hb_svg_blob_meta_t *meta)
-{
-  if (!meta)
-    return;
-  if (!meta->transferred && meta->data)
-    hb_free (meta->data);
-  meta->data = nullptr;
-  meta->allocated = 0;
-  meta->transferred = true;
-}
-
-static void
-hb_svg_blob_meta_destroy (void *data)
-{
-  auto *meta = (hb_svg_blob_meta_t *) data;
-  hb_svg_blob_meta_release_buffer (meta);
-  if (meta->in_replace)
-  {
-    meta->in_replace = false;
-    return;
-  }
-  hb_free (meta);
-}
-
-static hb_blob_t *
-hb_svg_blob_from_buffer (hb_blob_t **recycled_blob,
-			 hb_vector_t<char> *buf)
-{
-  unsigned len = 0;
-  int allocated = 0;
-  char *data = buf->steal (&len, &allocated);
-  if (!data)
-    return nullptr;
-
-  hb_blob_t *blob = nullptr;
-  if (*recycled_blob)
-    blob = *recycled_blob;
-  bool reused_blob = blob && blob != hb_blob_get_empty ();
-  bool new_meta = false;
-  auto *meta = reused_blob
-             ? (hb_svg_blob_meta_t *) hb_blob_get_user_data (blob, &hb_svg_blob_meta_user_data_key)
-             : nullptr;
-  if (!meta)
-  {
-    meta = (hb_svg_blob_meta_t *) hb_malloc (sizeof (hb_svg_blob_meta_t));
-    if (!meta)
-    {
-      hb_free (data);
-      return nullptr;
-    }
-    meta->data = nullptr;
-    meta->allocated = 0;
-    meta->transferred = true;
-    meta->in_replace = false;
-    new_meta = true;
-  }
-
-  if (reused_blob)
-  {
-    /* replace_buffer() first destroys old buffer user_data; keep meta alive. */
-    meta->in_replace = true;
-    blob->replace_buffer (data, len, HB_MEMORY_MODE_WRITABLE, meta, hb_svg_blob_meta_destroy);
-    hb_svg_blob_meta_set_buffer (meta, data, allocated);
-  }
-  else
-  {
-    hb_svg_blob_meta_set_buffer (meta, data, allocated);
-    blob = hb_blob_create_or_fail (data, len, HB_MEMORY_MODE_WRITABLE, meta, hb_svg_blob_meta_destroy);
-    if (unlikely (!blob))
-      return nullptr;
-  }
-
-  if (unlikely (blob == hb_blob_get_empty ()))
-  {
-    if (new_meta)
-      hb_free (meta);
-    hb_free (data);
-    return nullptr;
-  }
-
-  if (new_meta &&
-      !hb_blob_set_user_data (blob,
-			      &hb_svg_blob_meta_user_data_key,
-			      meta,
-			      nullptr,
-			      true))
-  {
-    if (!reused_blob)
-      hb_blob_destroy (blob);
-    return nullptr;
-  }
-
-  if (*recycled_blob)
-    *recycled_blob = nullptr;
-
-  return blob;
-}
-
-static void
-hb_svg_recover_recycled_buffer (hb_blob_t *blob,
-				hb_vector_t<char> *buf)
-{
-  if (!blob)
-    return;
-
-  auto *meta = (hb_svg_blob_meta_t *) hb_blob_get_user_data (blob, &hb_svg_blob_meta_user_data_key);
-  if (!meta || meta->transferred || !meta->data)
-    return;
-
-  buf->recycle_buffer (meta->data, 0, meta->allocated);
-  meta->data = nullptr;
-  meta->allocated = 0;
-  meta->transferred = true;
-}
-
-static void
-hb_svg_append_color (hb_vector_t<char> *buf,
-                     hb_color_t color,
-                     bool with_alpha)
-{
-  static const char hex[] = "0123456789ABCDEF";
-  unsigned r = hb_color_get_red (color);
-  unsigned g = hb_color_get_green (color);
-  unsigned b = hb_color_get_blue (color);
-  unsigned a = hb_color_get_alpha (color);
-  hb_svg_append_c (buf, '#');
-  if (((r >> 4) == (r & 0xF)) &&
-      ((g >> 4) == (g & 0xF)) &&
-      ((b >> 4) == (b & 0xF)))
-  {
-    hb_svg_append_c (buf, hex[r & 0xF]);
-    hb_svg_append_c (buf, hex[g & 0xF]);
-    hb_svg_append_c (buf, hex[b & 0xF]);
-  }
-  else
-  {
-    hb_svg_append_hex_byte (buf, r);
-    hb_svg_append_hex_byte (buf, g);
-    hb_svg_append_hex_byte (buf, b);
-  }
-  if (with_alpha && a != 255)
-  {
-    hb_svg_append_str (buf, "\" fill-opacity=\"");
-    hb_svg_append_num (buf, a / 255.f, 4);
-  }
-}
-
-static void
-hb_svg_transform_point (const hb_transform_t<> &t,
-                        float x_scale_factor,
-                        float y_scale_factor,
-                        float x, float y,
-                        float *tx, float *ty)
-{
-  float xx = x, yy = y;
-  t.transform_point (xx, yy);
-  *tx = xx / (x_scale_factor > 0 ? x_scale_factor : 1.f);
-  *ty = yy / (y_scale_factor > 0 ? y_scale_factor : 1.f);
-}
-
-static hb_bool_t
-hb_svg_set_glyph_extents_common (const hb_transform_t<> &transform,
-                                 float x_scale_factor,
-                                 float y_scale_factor,
-                                 const hb_glyph_extents_t *glyph_extents,
-                                 hb_vector_extents_t *extents,
-                                 hb_bool_t *has_extents)
-{
-  float x0 = (float) glyph_extents->x_bearing;
-  float y0 = (float) glyph_extents->y_bearing;
-  float x1 = x0 + glyph_extents->width;
-  float y1 = y0 + glyph_extents->height;
-
-  float px[4] = {x0, x0, x1, x1};
-  float py[4] = {y0, y1, y0, y1};
-
-  float tx, ty;
-  hb_svg_transform_point (transform, x_scale_factor, y_scale_factor, px[0], py[0], &tx, &ty);
-  float tx_min = tx, tx_max = tx;
-  float ty_min = ty, ty_max = ty;
-
-  for (unsigned i = 1; i < 4; i++)
-  {
-    hb_svg_transform_point (transform, x_scale_factor, y_scale_factor, px[i], py[i], &tx, &ty);
-    tx_min = hb_min (tx_min, tx);
-    tx_max = hb_max (tx_max, tx);
-    ty_min = hb_min (ty_min, ty);
-    ty_max = hb_max (ty_max, ty);
-  }
-
-  if (tx_max <= tx_min || ty_max <= ty_min)
-  {
-    return false;
-  }
-
-  if (*has_extents)
-  {
-    float x0 = hb_min (extents->x, tx_min);
-    float y0 = hb_min (extents->y, ty_min);
-    float x1 = hb_max (extents->x + extents->width, tx_max);
-    float y1 = hb_max (extents->y + extents->height, ty_max);
-    *extents = {x0, y0, x1 - x0, y1 - y0};
-  }
-  else
-  {
-    *extents = {tx_min, ty_min, tx_max - tx_min, ty_max - ty_min};
-    *has_extents = true;
-  }
-  return true;
-}
-
+#include "hb-vector-svg.hh"
 
 struct hb_svg_color_glyph_cache_key_t
 {
@@ -477,62 +170,6 @@
   return {glyph, palette, foreground};
 }
 
-static inline void
-hb_svg_append_instance_transform (hb_vector_t<char> *out,
-                                  unsigned precision,
-                                  float x_scale_factor,
-                                  float y_scale_factor,
-                                  float xx, float yx,
-                                  float xy, float yy,
-                                  float tx, float ty)
-{
-  unsigned sprec = hb_svg_scale_precision (precision);
-  if (xx == 1.f && yx == 0.f && xy == 0.f && yy == 1.f)
-  {
-    float sx = 1.f / x_scale_factor;
-    float sy = 1.f / y_scale_factor;
-    hb_svg_append_str (out, "translate(");
-    hb_svg_append_num (out, tx / x_scale_factor, precision);
-    hb_svg_append_c (out, ',');
-    hb_svg_append_num (out, -ty / y_scale_factor, precision);
-    hb_svg_append_str (out, ") scale(");
-    hb_svg_append_num (out, sx, sprec, true);
-    hb_svg_append_c (out, ',');
-    hb_svg_append_num (out, -sy, sprec, true);
-    hb_svg_append_c (out, ')');
-  }
-  else
-  {
-    hb_svg_append_str (out, "matrix(");
-    hb_svg_append_num (out, xx / x_scale_factor, sprec, true);
-    hb_svg_append_c (out, ',');
-    hb_svg_append_num (out, yx / y_scale_factor, sprec, true);
-    hb_svg_append_c (out, ',');
-    hb_svg_append_num (out, -xy / x_scale_factor, sprec, true);
-    hb_svg_append_c (out, ',');
-    hb_svg_append_num (out, -yy / y_scale_factor, sprec, true);
-    hb_svg_append_c (out, ',');
-    hb_svg_append_num (out, tx / x_scale_factor, precision);
-    hb_svg_append_c (out, ',');
-    hb_svg_append_num (out, -ty / y_scale_factor, precision);
-    hb_svg_append_c (out, ')');
-  }
-}
-
-static inline void
-hb_svg_append_image_instance_translate (hb_vector_t<char> *out,
-                                        unsigned precision,
-                                        float x_scale_factor,
-                                        float y_scale_factor,
-                                        float tx, float ty)
-{
-  hb_svg_append_str (out, "translate(");
-  hb_svg_append_num (out, tx / x_scale_factor, precision);
-  hb_svg_append_c (out, ',');
-  hb_svg_append_num (out, -ty / y_scale_factor, precision);
-  hb_svg_append_c (out, ')');
-}
-
 static hb_bool_t
 hb_svg_get_color_stops (hb_vector_paint_t *paint,
                         hb_color_line_t *color_line,
diff --git a/src/hb-vector-svg-subset.cc b/src/hb-vector-svg-subset.cc
index 54c25e2..b46f404 100644
--- a/src/hb-vector-svg-subset.cc
+++ b/src/hb-vector-svg-subset.cc
@@ -41,32 +41,7 @@
 #include <string.h>
 
 #ifndef HB_NO_SVG
-static bool
-hb_svg_append_str (hb_vector_t<char> *out,
-                   const char *s)
-{
-  return hb_svg_append_len (out, s, (unsigned) strlen (s));
-}
-
-static bool
-hb_svg_append_unsigned (hb_vector_t<char> *out,
-                        unsigned v)
-{
-  char tmp[10];
-  unsigned n = 0;
-  do {
-    tmp[n++] = (char) ('0' + (v % 10));
-    v /= 10;
-  } while (v);
-
-  unsigned old_len = out->length;
-  if (unlikely (!out->resize_dirty ((int) (old_len + n))))
-    return false;
-
-  for (unsigned i = 0; i < n; i++)
-    out->arrayZ[old_len + i] = tmp[n - 1 - i];
-  return true;
-}
+#include "hb-vector-svg.hh"
 
 static bool
 hb_svg_append_with_prefix (hb_vector_t<char> *out,
diff --git a/src/hb-vector-svg.hh b/src/hb-vector-svg.hh
new file mode 100644
index 0000000..418d5bc
--- /dev/null
+++ b/src/hb-vector-svg.hh
@@ -0,0 +1,366 @@
+#ifndef HB_VECTOR_SVG_HH
+#define HB_VECTOR_SVG_HH
+
+#include "hb-vector.h"
+
+static inline bool
+hb_svg_append_str (hb_vector_t<char> *buf, const char *s)
+{
+  return hb_svg_append_len (buf, s, (unsigned) strlen (s));
+}
+
+static inline bool
+hb_svg_append_unsigned (hb_vector_t<char> *buf, unsigned v)
+{
+  char tmp[10];
+  unsigned n = 0;
+  do {
+    tmp[n++] = (char) ('0' + (v % 10));
+    v /= 10;
+  } while (v);
+
+  unsigned old_len = buf->length;
+  if (unlikely (!buf->resize_dirty ((int) (old_len + n))))
+    return false;
+
+  for (unsigned i = 0; i < n; i++)
+    buf->arrayZ[old_len + i] = tmp[n - 1 - i];
+  return true;
+}
+
+static inline bool
+hb_svg_append_hex_byte (hb_vector_t<char> *buf, unsigned v)
+{
+  static const char hex[] = "0123456789ABCDEF";
+  char tmp[2] = {hex[(v >> 4) & 15], hex[v & 15]};
+  return hb_svg_append_len (buf, tmp, 2);
+}
+
+static inline bool
+hb_svg_append_base64 (hb_vector_t<char> *buf,
+                      const uint8_t *data,
+                      unsigned len)
+{
+  static const char b64[] =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+  unsigned out_len = ((len + 2) / 3) * 4;
+  unsigned old_len = buf->length;
+  if (unlikely (!buf->resize_dirty ((int) (old_len + out_len))))
+    return false;
+
+  char *dst = buf->arrayZ + old_len;
+  unsigned di = 0;
+  unsigned i = 0;
+  while (i + 2 < len)
+  {
+    unsigned v = ((unsigned) data[i] << 16) |
+                 ((unsigned) data[i + 1] << 8) |
+                 ((unsigned) data[i + 2]);
+    dst[di++] = b64[(v >> 18) & 63];
+    dst[di++] = b64[(v >> 12) & 63];
+    dst[di++] = b64[(v >> 6) & 63];
+    dst[di++] = b64[v & 63];
+    i += 3;
+  }
+
+  if (i < len)
+  {
+    unsigned v = (unsigned) data[i] << 16;
+    if (i + 1 < len)
+      v |= (unsigned) data[i + 1] << 8;
+    dst[di++] = b64[(v >> 18) & 63];
+    dst[di++] = b64[(v >> 12) & 63];
+    dst[di++] = (i + 1 < len) ? b64[(v >> 6) & 63] : '=';
+    dst[di++] = '=';
+  }
+
+  return true;
+}
+
+struct hb_svg_blob_meta_t
+{
+  char *data;
+  int allocated;
+  bool transferred;
+  bool in_replace;
+};
+
+static hb_user_data_key_t hb_svg_blob_meta_user_data_key;
+
+static inline void
+hb_svg_blob_meta_set_buffer (hb_svg_blob_meta_t *meta,
+                             char *data,
+                             int allocated)
+{
+  meta->data = data;
+  meta->allocated = allocated;
+  meta->transferred = false;
+}
+
+static inline void
+hb_svg_blob_meta_release_buffer (hb_svg_blob_meta_t *meta)
+{
+  if (!meta)
+    return;
+  if (!meta->transferred && meta->data)
+    hb_free (meta->data);
+  meta->data = nullptr;
+  meta->allocated = 0;
+  meta->transferred = true;
+}
+
+static inline void
+hb_svg_blob_meta_destroy (void *data)
+{
+  auto *meta = (hb_svg_blob_meta_t *) data;
+  hb_svg_blob_meta_release_buffer (meta);
+  if (meta->in_replace)
+  {
+    meta->in_replace = false;
+    return;
+  }
+  hb_free (meta);
+}
+
+static inline hb_blob_t *
+hb_svg_blob_from_buffer (hb_blob_t **recycled_blob,
+                         hb_vector_t<char> *buf)
+{
+  unsigned len = 0;
+  int allocated = 0;
+  char *data = buf->steal (&len, &allocated);
+  if (!data)
+    return nullptr;
+
+  hb_blob_t *blob = nullptr;
+  if (*recycled_blob)
+    blob = *recycled_blob;
+  bool reused_blob = blob && blob != hb_blob_get_empty ();
+  bool new_meta = false;
+  auto *meta = reused_blob
+             ? (hb_svg_blob_meta_t *) hb_blob_get_user_data (blob, &hb_svg_blob_meta_user_data_key)
+             : nullptr;
+  if (!meta)
+  {
+    meta = (hb_svg_blob_meta_t *) hb_malloc (sizeof (hb_svg_blob_meta_t));
+    if (!meta)
+    {
+      hb_free (data);
+      return nullptr;
+    }
+    meta->data = nullptr;
+    meta->allocated = 0;
+    meta->transferred = true;
+    meta->in_replace = false;
+    new_meta = true;
+  }
+
+  if (reused_blob)
+  {
+    meta->in_replace = true;
+    blob->replace_buffer (data, len, HB_MEMORY_MODE_WRITABLE, meta, hb_svg_blob_meta_destroy);
+    hb_svg_blob_meta_set_buffer (meta, data, allocated);
+  }
+  else
+  {
+    hb_svg_blob_meta_set_buffer (meta, data, allocated);
+    blob = hb_blob_create_or_fail (data, len, HB_MEMORY_MODE_WRITABLE, meta, hb_svg_blob_meta_destroy);
+    if (unlikely (!blob))
+      return nullptr;
+  }
+
+  if (unlikely (blob == hb_blob_get_empty ()))
+  {
+    if (new_meta)
+      hb_free (meta);
+    hb_free (data);
+    return nullptr;
+  }
+
+  if (new_meta &&
+      !hb_blob_set_user_data (blob,
+                              &hb_svg_blob_meta_user_data_key,
+                              meta,
+                              nullptr,
+                              true))
+  {
+    if (!reused_blob)
+      hb_blob_destroy (blob);
+    return nullptr;
+  }
+
+  if (*recycled_blob)
+    *recycled_blob = nullptr;
+
+  return blob;
+}
+
+static inline void
+hb_svg_recover_recycled_buffer (hb_blob_t *blob,
+                                hb_vector_t<char> *buf)
+{
+  if (!blob)
+    return;
+
+  auto *meta = (hb_svg_blob_meta_t *) hb_blob_get_user_data (blob, &hb_svg_blob_meta_user_data_key);
+  if (!meta || meta->transferred || !meta->data)
+    return;
+
+  buf->recycle_buffer (meta->data, 0, meta->allocated);
+  meta->data = nullptr;
+  meta->allocated = 0;
+  meta->transferred = true;
+}
+
+static inline void
+hb_svg_append_color (hb_vector_t<char> *buf,
+                     hb_color_t color,
+                     bool with_alpha)
+{
+  static const char hex[] = "0123456789ABCDEF";
+  unsigned r = hb_color_get_red (color);
+  unsigned g = hb_color_get_green (color);
+  unsigned b = hb_color_get_blue (color);
+  unsigned a = hb_color_get_alpha (color);
+  hb_svg_append_c (buf, '#');
+  if (((r >> 4) == (r & 0xF)) &&
+      ((g >> 4) == (g & 0xF)) &&
+      ((b >> 4) == (b & 0xF)))
+  {
+    hb_svg_append_c (buf, hex[r & 0xF]);
+    hb_svg_append_c (buf, hex[g & 0xF]);
+    hb_svg_append_c (buf, hex[b & 0xF]);
+  }
+  else
+  {
+    hb_svg_append_hex_byte (buf, r);
+    hb_svg_append_hex_byte (buf, g);
+    hb_svg_append_hex_byte (buf, b);
+  }
+  if (with_alpha && a != 255)
+  {
+    hb_svg_append_str (buf, "\" fill-opacity=\"");
+    hb_svg_append_num (buf, a / 255.f, 4);
+  }
+}
+
+static inline void
+hb_svg_transform_point (const hb_transform_t<> &t,
+                        float x_scale_factor,
+                        float y_scale_factor,
+                        float x, float y,
+                        float *tx, float *ty)
+{
+  float xx = x, yy = y;
+  t.transform_point (xx, yy);
+  *tx = xx / (x_scale_factor > 0 ? x_scale_factor : 1.f);
+  *ty = yy / (y_scale_factor > 0 ? y_scale_factor : 1.f);
+}
+
+static inline hb_bool_t
+hb_svg_set_glyph_extents_common (const hb_transform_t<> &transform,
+                                 float x_scale_factor,
+                                 float y_scale_factor,
+                                 const hb_glyph_extents_t *glyph_extents,
+                                 hb_vector_extents_t *extents,
+                                 hb_bool_t *has_extents)
+{
+  float x0 = (float) glyph_extents->x_bearing;
+  float y0 = (float) glyph_extents->y_bearing;
+  float x1 = x0 + glyph_extents->width;
+  float y1 = y0 + glyph_extents->height;
+
+  float px[4] = {x0, x0, x1, x1};
+  float py[4] = {y0, y1, y0, y1};
+
+  float tx, ty;
+  hb_svg_transform_point (transform, x_scale_factor, y_scale_factor, px[0], py[0], &tx, &ty);
+  float tx_min = tx, tx_max = tx;
+  float ty_min = ty, ty_max = ty;
+
+  for (unsigned i = 1; i < 4; i++)
+  {
+    hb_svg_transform_point (transform, x_scale_factor, y_scale_factor, px[i], py[i], &tx, &ty);
+    tx_min = hb_min (tx_min, tx);
+    tx_max = hb_max (tx_max, tx);
+    ty_min = hb_min (ty_min, ty);
+    ty_max = hb_max (ty_max, ty);
+  }
+
+  if (tx_max <= tx_min || ty_max <= ty_min)
+    return false;
+
+  if (*has_extents)
+  {
+    float x0 = hb_min (extents->x, tx_min);
+    float y0 = hb_min (extents->y, ty_min);
+    float x1 = hb_max (extents->x + extents->width, tx_max);
+    float y1 = hb_max (extents->y + extents->height, ty_max);
+    *extents = {x0, y0, x1 - x0, y1 - y0};
+  }
+  else
+  {
+    *extents = {tx_min, ty_min, tx_max - tx_min, ty_max - ty_min};
+    *has_extents = true;
+  }
+  return true;
+}
+
+static inline void
+hb_svg_append_instance_transform (hb_vector_t<char> *out,
+                                  unsigned precision,
+                                  float x_scale_factor,
+                                  float y_scale_factor,
+                                  float xx, float yx,
+                                  float xy, float yy,
+                                  float tx, float ty)
+{
+  unsigned sprec = hb_svg_scale_precision (precision);
+  if (xx == 1.f && yx == 0.f && xy == 0.f && yy == 1.f)
+  {
+    float sx = 1.f / x_scale_factor;
+    float sy = 1.f / y_scale_factor;
+    hb_svg_append_str (out, "translate(");
+    hb_svg_append_num (out, tx / x_scale_factor, precision);
+    hb_svg_append_c (out, ',');
+    hb_svg_append_num (out, -ty / y_scale_factor, precision);
+    hb_svg_append_str (out, ") scale(");
+    hb_svg_append_num (out, sx, sprec, true);
+    hb_svg_append_c (out, ',');
+    hb_svg_append_num (out, -sy, sprec, true);
+    hb_svg_append_c (out, ')');
+  }
+  else
+  {
+    hb_svg_append_str (out, "matrix(");
+    hb_svg_append_num (out, xx / x_scale_factor, sprec, true);
+    hb_svg_append_c (out, ',');
+    hb_svg_append_num (out, yx / y_scale_factor, sprec, true);
+    hb_svg_append_c (out, ',');
+    hb_svg_append_num (out, -xy / x_scale_factor, sprec, true);
+    hb_svg_append_c (out, ',');
+    hb_svg_append_num (out, -yy / y_scale_factor, sprec, true);
+    hb_svg_append_c (out, ',');
+    hb_svg_append_num (out, tx / x_scale_factor, precision);
+    hb_svg_append_c (out, ',');
+    hb_svg_append_num (out, -ty / y_scale_factor, precision);
+    hb_svg_append_c (out, ')');
+  }
+}
+
+static inline void
+hb_svg_append_image_instance_translate (hb_vector_t<char> *out,
+                                        unsigned precision,
+                                        float x_scale_factor,
+                                        float y_scale_factor,
+                                        float tx, float ty)
+{
+  hb_svg_append_str (out, "translate(");
+  hb_svg_append_num (out, tx / x_scale_factor, precision);
+  hb_svg_append_c (out, ',');
+  hb_svg_append_num (out, -ty / y_scale_factor, precision);
+  hb_svg_append_c (out, ')');
+}
+
+#endif /* HB_VECTOR_SVG_HH */
diff --git a/src/meson.build b/src/meson.build
index 156a770..a92e46f 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -587,6 +587,7 @@
   'GRAPHITE',
   'ICU',
   'RASTER',
+  'SUBSET',
   'UNISCRIBE',
   'VECTOR',
   'WASM',