[util/draw] Render color glyphs if cairo API available
diff --git a/configure.ac b/configure.ac
index 24048cf..6ab2725 100644
--- a/configure.ac
+++ b/configure.ac
@@ -194,6 +194,10 @@
 have_cairo=false
 if test "x$with_cairo" = "xyes" -o "x$with_cairo" = "xauto"; then
 	PKG_CHECK_MODULES(CAIRO, cairo >= 1.8.0, have_cairo=true, :)
+	save_libs=$LIBS
+	LIBS="$LIBS $CAIRO_LIBS"
+	AC_CHECK_FUNCS(cairo_user_font_face_set_render_color_glyph_func)
+	LIBS=$save_libs
 fi
 if test "x$with_cairo" = "xyes" -a "x$have_cairo" != "xtrue"; then
 	AC_MSG_ERROR([cairo support requested but not found])
diff --git a/meson.build b/meson.build
index 2531ef0..6fbbcb4 100644
--- a/meson.build
+++ b/meson.build
@@ -186,6 +186,7 @@
 
 if cairo_dep.found()
   conf.set('HAVE_CAIRO', 1)
+  check_funcs += [['cairo_user_font_face_set_render_color_glyph_func', {'deps': cairo_dep}]]
 endif
 
 if cairo_ft_dep.found()
diff --git a/util/helper-cairo-user.hh b/util/helper-cairo-user.hh
index c1f8c46..075db0a 100644
--- a/util/helper-cairo-user.hh
+++ b/util/helper-cairo-user.hh
@@ -32,6 +32,8 @@
 #include <cairo.h>
 #include <hb.h>
 
+#include "hb-blob.hh"
+
 static void
 move_to (hb_draw_funcs_t *dfuncs,
 	 cairo_t *cr,
@@ -112,15 +114,99 @@
   return CAIRO_STATUS_SUCCESS;
 }
 
+#ifdef HAVE_CAIRO_USER_FONT_FACE_SET_RENDER_COLOR_GLYPH_FUNC
+
+static cairo_status_t
+_hb_bytes_read_func (hb_bytes_t	*src,
+		     char	*data,
+		     unsigned	 length)
+{
+  if (unlikely (src->length < length))
+    return CAIRO_STATUS_READ_ERROR;
+
+  memcpy (data, src->arrayZ, length);
+  *src += length;
+
+  return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+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_font_face_get_user_data (cairo_scaled_font_get_font_face (scaled_font),
+								  &_hb_font_cairo_user_data_key));
+
+  hb_blob_t *blob = hb_ot_color_glyph_reference_png (font, glyph);
+  if (blob == hb_blob_get_empty ())
+    return CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED;
+
+  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);
+
+#ifdef CAIRO_HAS_PNG_FUNCTIONS
+  /* Draw PNG. */
+  hb_bytes_t bytes = blob->as_bytes ();
+  cairo_surface_t *surface = cairo_image_surface_create_from_png_stream ((cairo_read_func_t) _hb_bytes_read_func,
+									 std::addressof (bytes));
+  hb_blob_destroy (blob);
+
+  if (unlikely (cairo_surface_status (surface)) != CAIRO_STATUS_SUCCESS)
+  {
+    cairo_surface_destroy (surface);
+    return CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED;
+  }
+
+  int width = cairo_image_surface_get_width (surface);
+  int height = cairo_image_surface_get_width (surface);
+
+  hb_glyph_extents_t hb_extents;
+  if (unlikely (!hb_font_get_glyph_extents (font, glyph, &hb_extents)))
+  {
+    cairo_surface_destroy (surface);
+    return CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED;
+  }
+
+  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);
+
+  cairo_translate (cr, hb_extents.x_bearing, hb_extents.y_bearing);
+  cairo_scale (cr, hb_extents.width, hb_extents.height);
+  cairo_set_source (cr, pattern);
+
+  cairo_rectangle (cr, 0, 0, 1, 1);
+  cairo_fill (cr);
+  cairo_pattern_destroy (pattern);
+
+  cairo_surface_destroy (surface);
+  return CAIRO_STATUS_SUCCESS;
+#endif
+  return CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED;
+}
+#endif
+
 static inline cairo_font_face_t *
 helper_cairo_create_user_font_face (const font_options_t *font_opts)
 {
   cairo_font_face_t *cairo_face = cairo_user_font_face_create ();
-  cairo_user_font_face_set_render_glyph_func (cairo_face, render_glyph);
+
   cairo_font_face_set_user_data (cairo_face,
 				 &_hb_font_cairo_user_data_key,
 				 hb_font_reference (font_opts->font),
 				 (cairo_destroy_func_t) hb_font_destroy);
+
+  cairo_user_font_face_set_render_glyph_func (cairo_face, render_glyph);
+#ifdef HAVE_CAIRO_USER_FONT_FACE_SET_RENDER_COLOR_GLYPH_FUNC
+  if (hb_ot_color_has_png (hb_font_get_face (font_opts->font)))
+    cairo_user_font_face_set_render_color_glyph_func (cairo_face, render_color_glyph);
+#endif
+
   return cairo_face;
 }