(root)/
harfbuzz-8.3.0/
test/
api/
test-paint.c
       1  /*
       2   * Copyright © 2022 Matthias Clasen
       3   *
       4   *  This is part of HarfBuzz, a text shaping library.
       5   *
       6   * Permission is hereby granted, without written agreement and without
       7   * license or royalty fees, to use, copy, modify, and distribute this
       8   * software and its documentation for any purpose, provided that the
       9   * above copyright notice and the following two paragraphs appear in
      10   * all copies of this software.
      11   *
      12   * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
      13   * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
      14   * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
      15   * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
      16   * DAMAGE.
      17   *
      18   * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
      19   * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
      20   * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
      21   * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
      22   * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
      23   */
      24  
      25  #include "hb-test.h"
      26  
      27  #include <hb-features.h>
      28  #include <hb-ot.h>
      29  
      30  #ifdef HB_HAS_FREETYPE
      31  #include <hb-ft.h>
      32  
      33  #if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 21300
      34  #include FT_COLOR_H
      35  #endif
      36  #endif
      37  
      38  static inline hb_bool_t
      39  have_ft_colrv1 (void)
      40  {
      41  #if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 21300
      42    return TRUE;
      43  #else
      44    return FALSE;
      45  #endif
      46  }
      47  
      48  /* Unit tests for hb-paint.h */
      49  
      50  /* ---- */
      51  
      52  typedef struct {
      53    int level;
      54    GString *string;
      55  } paint_data_t;
      56  
      57  static void print (paint_data_t *data, const char *format, ...) G_GNUC_PRINTF (2, 3);
      58  
      59  static void
      60  print (paint_data_t *data,
      61         const char *format,
      62         ...)
      63  {
      64    va_list args;
      65  
      66    g_string_append_printf (data->string, "%*s", 2 * data->level, "");
      67  
      68    va_start (args, format);
      69    g_string_append_vprintf (data->string, format, args);
      70    va_end (args);
      71  
      72    g_string_append (data->string, "\n");
      73  }
      74  
      75  static void
      76  push_transform (hb_paint_funcs_t *funcs,
      77                  void *paint_data,
      78                  float xx, float yx,
      79                  float xy, float yy,
      80                  float dx, float dy,
      81                  void *user_data)
      82  {
      83    paint_data_t *data = paint_data;
      84  
      85    print (data, "start transform %.3g %.3g %.3g %.3g %.3g %.3g", xx, yx, xy, yy, dx, dy);
      86    data->level++;
      87  }
      88  
      89  static void
      90  pop_transform (hb_paint_funcs_t *funcs,
      91                 void *paint_data,
      92                 void *user_data)
      93  {
      94    paint_data_t *data = paint_data;
      95  
      96    data->level--;
      97    print (data, "end transform");
      98  }
      99  
     100  static hb_bool_t
     101  paint_color_glyph (hb_paint_funcs_t *funcs,
     102                     void *paint_data,
     103                     hb_codepoint_t glyph,
     104                     hb_font_t *font,
     105                     void *user_data)
     106  {
     107    paint_data_t *data = paint_data;
     108  
     109    print (data, "paint color glyph %u; acting as failed", glyph);
     110  
     111    return FALSE;
     112  }
     113  
     114  static void
     115  push_clip_glyph (hb_paint_funcs_t *funcs,
     116                   void *paint_data,
     117                   hb_codepoint_t glyph,
     118                   hb_font_t *font,
     119                   void *user_data)
     120  {
     121    paint_data_t *data = paint_data;
     122  
     123    print (data, "start clip glyph %u", glyph);
     124    data->level++;
     125  }
     126  
     127  static void
     128  push_clip_rectangle (hb_paint_funcs_t *funcs,
     129                       void *paint_data,
     130                       float xmin, float ymin, float xmax, float ymax,
     131                       void *user_data)
     132  {
     133    paint_data_t *data = paint_data;
     134  
     135    print (data, "start clip rectangle %.3g %.3g %.3g %.3g", xmin, ymin, xmax, ymax);
     136    data->level++;
     137  }
     138  
     139  static void
     140  pop_clip (hb_paint_funcs_t *funcs,
     141            void *paint_data,
     142            void *user_data)
     143  {
     144    paint_data_t *data = paint_data;
     145  
     146    data->level--;
     147    print (data, "end clip");
     148  }
     149  
     150  static void
     151  paint_color (hb_paint_funcs_t *funcs,
     152               void *paint_data,
     153               hb_bool_t use_foreground,
     154               hb_color_t color,
     155               void *user_data)
     156  {
     157    paint_data_t *data = paint_data;
     158  
     159    print (data, "solid %d %d %d %d",
     160           hb_color_get_red (color),
     161           hb_color_get_green (color),
     162           hb_color_get_blue (color),
     163           hb_color_get_alpha (color));
     164  }
     165  
     166  static hb_bool_t
     167  paint_image (hb_paint_funcs_t *funcs,
     168               void *paint_data,
     169               hb_blob_t *blob,
     170               unsigned int width,
     171               unsigned int height,
     172               hb_tag_t format,
     173               float slant,
     174               hb_glyph_extents_t *extents,
     175               void *user_data)
     176  {
     177    paint_data_t *data = paint_data;
     178    char buf[5] = { 0, };
     179  
     180    hb_tag_to_string (format, buf);
     181    print (data, "image type %s size %u %u slant %.3g extents %d %d %d %d\n",
     182           buf, width, height, slant,
     183           extents->x_bearing, extents->y_bearing, extents->width, extents->height);
     184  
     185    return TRUE;
     186  }
     187  
     188  static void
     189  print_color_line (paint_data_t *data,
     190                    hb_color_line_t *color_line)
     191  {
     192    hb_color_stop_t *stops;
     193    unsigned int len;
     194  
     195    len = hb_color_line_get_color_stops (color_line, 0, NULL, NULL);
     196    stops = alloca (len * sizeof (hb_color_stop_t));
     197    hb_color_line_get_color_stops (color_line, 0, &len, stops);
     198  
     199    print (data, "colors %d", hb_color_line_get_extend (color_line));
     200    data->level += 1;
     201    for (unsigned int i = 0; i < len; i++)
     202      print (data, "%.3g %d %d %d %d",
     203             stops[i].offset,
     204             hb_color_get_red (stops[i].color),
     205             hb_color_get_green (stops[i].color),
     206             hb_color_get_blue (stops[i].color),
     207             hb_color_get_alpha (stops[i].color));
     208    data->level -= 1;
     209  }
     210  
     211  static void
     212  paint_linear_gradient (hb_paint_funcs_t *funcs,
     213                         void *paint_data,
     214                         hb_color_line_t *color_line,
     215                         float x0, float y0,
     216                         float x1, float y1,
     217                         float x2, float y2,
     218                         void *user_data)
     219  {
     220    paint_data_t *data = paint_data;
     221  
     222    print (data, "linear gradient");
     223    data->level += 1;
     224    print (data, "p0 %.3g %.3g", x0, y0);
     225    print (data, "p1 %.3g %.3g", x1, y1);
     226    print (data, "p2 %.3g %.3g", x2, y2);
     227  
     228    print_color_line (data, color_line);
     229    data->level -= 1;
     230  }
     231  
     232  static void
     233  paint_radial_gradient (hb_paint_funcs_t *funcs,
     234                         void *paint_data,
     235                         hb_color_line_t *color_line,
     236                         float x0, float y0, float r0,
     237                         float x1, float y1, float r1,
     238                         void *user_data)
     239  {
     240    paint_data_t *data = paint_data;
     241  
     242    print (data, "radial gradient");
     243    data->level += 1;
     244    print (data, "p0 %.3g %.3g radius %.3g", x0, y0, r0);
     245    print (data, "p1 %.3g %.3g radius %.3g", x1, y1, r1);
     246  
     247    print_color_line (data, color_line);
     248    data->level -= 1;
     249  }
     250  
     251  static void
     252  paint_sweep_gradient (hb_paint_funcs_t *funcs,
     253                        void *paint_data,
     254                        hb_color_line_t *color_line,
     255                        float cx, float cy,
     256                        float start_angle,
     257                        float end_angle,
     258                        void *user_data)
     259  {
     260    paint_data_t *data = paint_data;
     261  
     262    print (data, "sweep gradient");
     263    data->level++;
     264    print (data, "center %.3g %.3g", cx, cy);
     265    print (data, "angles %.3g %.3g", start_angle, end_angle);
     266  
     267    print_color_line (data, color_line);
     268    data->level -= 1;
     269  }
     270  
     271  static void
     272  push_group (hb_paint_funcs_t *funcs,
     273              void *paint_data,
     274              void *user_data)
     275  {
     276    paint_data_t *data = paint_data;
     277    print (data, "push group");
     278    data->level++;
     279  }
     280  
     281  static void
     282  pop_group (hb_paint_funcs_t *funcs,
     283             void *paint_data,
     284             hb_paint_composite_mode_t mode,
     285             void *user_data)
     286  {
     287    paint_data_t *data = paint_data;
     288    data->level--;
     289    print (data, "pop group mode %d", mode);
     290  }
     291  
     292  static hb_paint_funcs_t *
     293  get_test_paint_funcs (void)
     294  {
     295    static hb_paint_funcs_t *funcs = NULL;
     296  
     297    if (!funcs)
     298    {
     299      funcs = hb_paint_funcs_create ();
     300  
     301      hb_paint_funcs_set_push_transform_func (funcs, push_transform, NULL, NULL);
     302      hb_paint_funcs_set_pop_transform_func (funcs, pop_transform, NULL, NULL);
     303      hb_paint_funcs_set_color_glyph_func (funcs, paint_color_glyph, NULL, NULL);
     304      hb_paint_funcs_set_push_clip_glyph_func (funcs, push_clip_glyph, NULL, NULL);
     305      hb_paint_funcs_set_push_clip_rectangle_func (funcs, push_clip_rectangle, NULL, NULL);
     306      hb_paint_funcs_set_pop_clip_func (funcs, pop_clip, NULL, NULL);
     307      hb_paint_funcs_set_push_group_func (funcs, push_group, NULL, NULL);
     308      hb_paint_funcs_set_pop_group_func (funcs, pop_group, NULL, NULL);
     309      hb_paint_funcs_set_color_func (funcs, paint_color, NULL, NULL);
     310      hb_paint_funcs_set_image_func (funcs, paint_image, NULL, NULL);
     311      hb_paint_funcs_set_linear_gradient_func (funcs, paint_linear_gradient, NULL, NULL);
     312      hb_paint_funcs_set_radial_gradient_func (funcs, paint_radial_gradient, NULL, NULL);
     313      hb_paint_funcs_set_sweep_gradient_func (funcs, paint_sweep_gradient, NULL, NULL);
     314  
     315      hb_paint_funcs_make_immutable (funcs);
     316    }
     317  
     318    return funcs;
     319  }
     320  
     321  typedef struct {
     322    const char *font_file;
     323    float slant;
     324    hb_codepoint_t glyph;
     325    unsigned int palette;
     326    const char *output;
     327  } paint_test_t;
     328  
     329  #define NOTO_HAND   "fonts/noto_handwriting-cff2_colr_1.otf"
     330  #define TEST_GLYPHS "fonts/test_glyphs-glyf_colr_1.ttf"
     331  #define TEST_GLYPHS_VF "fonts/test_glyphs-glyf_colr_1_variable.ttf"
     332  #define BAD_COLRV1  "fonts/bad_colrv1.ttf"
     333  #define ROCHER_ABC  "fonts/RocherColorGX.abc.ttf"
     334  
     335  /* To verify the rendering visually, use
     336   *
     337   * hb-view --font-slant SLANT --font-palette PALETTE FONT --glyphs [gidGID=0+1000]
     338   *
     339   * where GID is the glyph value of the test.
     340   */
     341  static paint_test_t paint_tests[] = {
     342    /* COLRv1 */
     343    { NOTO_HAND,   0.,  10,   0, "hand-10" },
     344    { NOTO_HAND,   0.2f,10,   0, "hand-10.2" },
     345  
     346    { TEST_GLYPHS, 0,    6,   0, "test-6" },   // linear gradient
     347    { TEST_GLYPHS, 0,   10,   0, "test-10" },  // sweep gradient
     348    { TEST_GLYPHS, 0,   92,   0, "test-92" },  // radial gradient
     349    { TEST_GLYPHS, 0,  106,   0, "test-106" },
     350    { TEST_GLYPHS, 0,  116,   0, "test-116" }, // compositing
     351    { TEST_GLYPHS, 0,  123,   0, "test-123" },
     352    { TEST_GLYPHS, 0,  154,   0, "test-154" },
     353    { TEST_GLYPHS, 0,  165,   0, "test-165" }, // linear gradient
     354    { TEST_GLYPHS, 0,  175,   0, "test-175" }, // layers
     355  
     356    { TEST_GLYPHS_VF, 0,    6,   0, "testvf-6" },
     357    { TEST_GLYPHS_VF, 0,   10,   0, "testvf-10" },
     358    { TEST_GLYPHS_VF, 0,   92,   0, "testvf-92" },
     359    { TEST_GLYPHS_VF, 0,  106,   0, "testvf-106" },
     360    { TEST_GLYPHS_VF, 0,  116,   0, "testvf-116" },
     361    { TEST_GLYPHS_VF, 0,  123,   0, "testvf-123" },
     362    { TEST_GLYPHS_VF, 0,  154,   0, "testvf-154" },
     363    { TEST_GLYPHS_VF, 0,  165,   0, "testvf-165" },
     364    { TEST_GLYPHS_VF, 0,  175,   0, "testvf-175" },
     365  
     366    { BAD_COLRV1,  0,  154,   0, "bad-154" },  // recursion
     367  
     368    /* COLRv0 */
     369    { ROCHER_ABC, 0.3f, 1,   0, "rocher-1" },
     370    { ROCHER_ABC, 0.3f, 2,   2, "rocher-2" },
     371    { ROCHER_ABC, 0,    3, 200, "rocher-3" },
     372  };
     373  
     374  static void
     375  test_hb_paint (gconstpointer d,
     376                 hb_bool_t     use_ft)
     377  {
     378    const paint_test_t *test = d;
     379    hb_face_t *face;
     380    hb_font_t *font;
     381    hb_paint_funcs_t *funcs;
     382    paint_data_t data;
     383    char *file;
     384    char *buffer;
     385    gsize len;
     386    GError *error = NULL;
     387  
     388    face = hb_test_open_font_file (test->font_file);
     389    font = hb_font_create (face);
     390  
     391    hb_font_set_synthetic_slant (font, test->slant);
     392  
     393  #ifdef HB_HAS_FREETYPE
     394    if (use_ft)
     395      hb_ft_font_set_funcs (font);
     396  #endif
     397  
     398    funcs = get_test_paint_funcs ();
     399  
     400    data.string = g_string_new ("");
     401    data.level = 0;
     402  
     403    hb_font_paint_glyph (font, test->glyph, funcs, &data, 0, HB_COLOR (0, 0, 0, 255));
     404  
     405    /* Run
     406     *
     407     * GENERATE_DATA=1 G_TEST_SRCDIR=./test/api ./build/test/api/test-paint -p TESTCASE > test/api/results/OUTPUT
     408     *
     409     * to produce the expected results file.
     410     */
     411    if (getenv ("GENERATE_DATA"))
     412      {
     413        g_print ("%s", data.string->str);
     414        exit (0);
     415      }
     416  
     417    file = g_test_build_filename (G_TEST_DIST, "results", test->output, NULL);
     418    if (!g_file_get_contents (file, &buffer, &len, &error))
     419    {
     420      g_test_message ("File %s not found.", file);
     421      g_test_fail ();
     422      return;
     423    }
     424  
     425    char **lines = g_strsplit (data.string->str, "\n", 0);
     426    char **expected;
     427    if (strstr (buffer, "\r\n"))
     428      expected = g_strsplit (buffer, "\r\n", 0);
     429    else
     430      expected = g_strsplit (buffer, "\n", 0);
     431  
     432    /* Strip initial comments */
     433    int i;
     434    for (i = 0; expected[i]; i++)
     435      {
     436        if (expected[i][0] != '#')
     437          {
     438            if (i > 0)
     439              {
     440                char **tmp = g_strdupv (expected + i);
     441                g_strfreev (expected);
     442                expected = tmp;
     443              }
     444            break;
     445          }
     446      }
     447  
     448    if (g_strv_length (lines) != g_strv_length (expected))
     449    {
     450      g_test_message ("Unexpected number of lines in output (%d instead of %d):\n%s", g_strv_length (lines), g_strv_length (expected), data.string->str);
     451      g_test_fail ();
     452    }
     453    else
     454    {
     455      unsigned int length = g_strv_length (lines);
     456      for (unsigned int i = 0; i < length; i++)
     457      {
     458        if (strcmp (lines[i], expected[i]) != 0)
     459        {
     460          int pos;
     461          for (pos = 0; lines[i][pos]; pos++)
     462            if (lines[i][pos] != expected[i][pos])
     463              break;
     464  
     465          g_test_message ("Unexpected output at %d:%d (%c instead of %c):\n%s", i, pos, lines[i][pos], expected[i][pos], data.string->str);
     466          g_test_fail ();
     467        }
     468      }
     469    }
     470  
     471    g_strfreev (lines);
     472    g_strfreev (expected);
     473  
     474    g_free (buffer);
     475    g_free (file);
     476  
     477    g_string_free (data.string, TRUE);
     478  
     479    hb_font_destroy (font);
     480    hb_face_destroy (face);
     481  }
     482  
     483  static void
     484  test_compare_ot_ft (const char *file, hb_codepoint_t glyph)
     485  {
     486    hb_face_t *face;
     487    hb_font_t *font;
     488    hb_paint_funcs_t *funcs;
     489    GString *ot_str;
     490    paint_data_t data;
     491  
     492    face = hb_test_open_font_file (file);
     493    font = hb_font_create (face);
     494  
     495    funcs = get_test_paint_funcs ();
     496  
     497    data.string = g_string_new ("");
     498    data.level = 0;
     499  
     500    hb_font_paint_glyph (font, glyph, funcs, &data, 0, HB_COLOR (0, 0, 0, 255));
     501  
     502    g_assert_true (data.level == 0);
     503  
     504    ot_str = data.string;
     505  
     506  #ifdef HB_HAS_FREETYPE
     507    hb_ft_font_set_funcs (font);
     508  #endif
     509  
     510    data.string = g_string_new ("");
     511    data.level = 0;
     512  
     513    hb_font_paint_glyph (font, glyph, funcs, &data, 0, HB_COLOR (0, 0, 0, 255));
     514  
     515    g_assert_true (data.level == 0);
     516  
     517    g_assert_cmpstr (ot_str->str, ==, data.string->str);
     518  
     519    g_string_free (data.string, TRUE);
     520    hb_font_destroy (font);
     521    hb_face_destroy (face);
     522  
     523    g_string_free (ot_str, TRUE);
     524  }
     525  
     526  static void
     527  test_hb_paint_ot (gconstpointer data)
     528  {
     529    test_hb_paint (data, 0);
     530  }
     531  
     532  static void
     533  test_hb_paint_ft (gconstpointer data)
     534  {
     535    if (have_ft_colrv1 ())
     536      test_hb_paint (data, 1);
     537    else
     538      g_test_skip ("FreeType COLRv1 support not present");
     539  }
     540  
     541  static void
     542  test_compare_ot_ft_novf (gconstpointer d)
     543  {
     544    if (have_ft_colrv1 ())
     545      test_compare_ot_ft (TEST_GLYPHS, GPOINTER_TO_UINT (d));
     546    else
     547      g_test_skip ("FreeType COLRv1 support not present");
     548  }
     549  
     550  static void
     551  test_compare_ot_ft_vf (gconstpointer d)
     552  {
     553    if (have_ft_colrv1 ())
     554      test_compare_ot_ft (TEST_GLYPHS_VF, GPOINTER_TO_UINT (d));
     555    else
     556      g_test_skip ("FreeType COLRv1 support not present");
     557  }
     558  
     559  static void
     560  scrutinize_linear_gradient (hb_paint_funcs_t *funcs,
     561                              void *paint_data,
     562                              hb_color_line_t *color_line,
     563                              float x0, float y0,
     564                              float x1, float y1,
     565                              float x2, float y2,
     566                              void *user_data)
     567  {
     568    hb_bool_t *result = paint_data;
     569    hb_color_stop_t *stops;
     570    unsigned int len;
     571    hb_color_stop_t *stops2;
     572    unsigned int len2;
     573  
     574    *result = FALSE;
     575  
     576    len = hb_color_line_get_color_stops (color_line, 0, NULL, NULL);
     577    if (len == 0)
     578      return;
     579  
     580    stops = malloc (len * sizeof (hb_color_stop_t));
     581    stops2 = malloc (len * sizeof (hb_color_stop_t));
     582  
     583    hb_color_line_get_color_stops (color_line, 0, &len, stops);
     584    hb_color_line_get_color_stops (color_line, 0, &len, stops2);
     585  
     586    // check that we can get stops twice
     587    if (memcmp (stops, stops2, len * sizeof (hb_color_stop_t)) != 0)
     588    {
     589      free (stops);
     590      free (stops2);
     591      return;
     592    }
     593  
     594    // check that we can get a single stop in the middle
     595    len2 = 1;
     596    hb_color_line_get_color_stops (color_line, len - 1, &len2, stops2);
     597    if (memcmp (&stops[len - 1], stops2, sizeof (hb_color_stop_t)) != 0)
     598    {
     599      free (stops);
     600      free (stops2);
     601      return;
     602    }
     603  
     604    free (stops);
     605    free (stops2);
     606  
     607    *result = TRUE;
     608  }
     609  
     610  static void
     611  test_color_stops (hb_bool_t use_ft)
     612  {
     613    hb_face_t *face;
     614    hb_font_t *font;
     615    hb_paint_funcs_t *funcs;
     616    hb_bool_t result = FALSE;
     617  
     618    face = hb_test_open_font_file (NOTO_HAND);
     619    font = hb_font_create (face);
     620  
     621  #ifdef HB_HAS_FREETYPE
     622    if (use_ft)
     623      hb_ft_font_set_funcs (font);
     624  #endif
     625  
     626    funcs = hb_paint_funcs_create ();
     627    hb_paint_funcs_set_linear_gradient_func (funcs, scrutinize_linear_gradient, NULL, NULL);
     628  
     629    hb_font_paint_glyph (font, 10, funcs, &result, 0, HB_COLOR (0, 0, 0, 255));
     630  
     631    g_assert_true (result);
     632  
     633    hb_paint_funcs_destroy (funcs);
     634    hb_font_destroy (font);
     635    hb_face_destroy (face);
     636  }
     637  
     638  static void
     639  test_color_stops_ot (void)
     640  {
     641    test_color_stops (0);
     642  }
     643  
     644  static void
     645  test_color_stops_ft (void)
     646  {
     647    if (have_ft_colrv1 ())
     648      test_color_stops (1);
     649    else
     650      g_test_skip ("FreeType COLRv1 support not present");
     651  }
     652  
     653  int
     654  main (int argc, char **argv)
     655  {
     656    int status = 0;
     657  
     658    hb_test_init (&argc, &argv);
     659    for (unsigned int i = 0; i < G_N_ELEMENTS (paint_tests); i++)
     660    {
     661      hb_test_add_data_flavor (&paint_tests[i], paint_tests[i].output, test_hb_paint_ot);
     662      hb_test_add_data_flavor (&paint_tests[i], paint_tests[i].output, test_hb_paint_ft);
     663    }
     664  
     665    hb_face_t *face = hb_test_open_font_file (TEST_GLYPHS);
     666    unsigned glyph_count = hb_face_get_glyph_count (face);
     667    for (unsigned int i = 1; i < glyph_count; i++)
     668    {
     669      char buf[20];
     670      snprintf (buf, 20, "test-%u", i);
     671      hb_test_add_data_flavor (GUINT_TO_POINTER (i), buf, test_compare_ot_ft_novf);
     672      hb_test_add_data_flavor (GUINT_TO_POINTER (i), buf, test_compare_ot_ft_vf);
     673    }
     674    hb_face_destroy (face);
     675  
     676    hb_test_add (test_color_stops_ot);
     677    hb_test_add (test_color_stops_ft);
     678  
     679    status = hb_test_run();
     680  
     681    return status;
     682  }