[API] unicode: rework virtual functions for subclassing
authorRyan Lortie <desrt@desrt.ca>
Wed, 20 Apr 2011 04:19:20 +0000 (00:19 -0400)
committerBehdad Esfahbod <behdad@behdad.org>
Wed, 20 Apr 2011 04:19:20 +0000 (00:19 -0400)
Unicode data providers can now be subclassed, including support for
chain-up.  The interface should now be nicely bindable, as well.

Also fix glib unicode funcs that where broken after hb_script_t
changes.  Nicely caught by the test-unicode.c added in this commit.

src/hb-glib.c
src/hb-icu.c
src/hb-ot-shape.cc
src/hb-shape.cc
src/hb-unicode-private.h
src/hb-unicode.c
src/hb-unicode.h
test/Makefile.am
test/test-unicode.c [new file with mode: 0644]

index 56123f3..1905637 100644 (file)
 HB_BEGIN_DECLS
 
 
-static hb_codepoint_t hb_glib_get_mirroring (hb_codepoint_t unicode) { g_unichar_get_mirror_char (unicode, &unicode); return unicode; }
-static hb_unicode_general_category_t hb_glib_get_general_category (hb_codepoint_t unicode) { return g_unichar_type (unicode); }
-static hb_script_t hb_glib_get_script (hb_codepoint_t unicode) { return g_unichar_get_script (unicode); }
-static unsigned int hb_glib_get_combining_class (hb_codepoint_t unicode) { return g_unichar_combining_class (unicode); }
-static unsigned int hb_glib_get_eastasian_width (hb_codepoint_t unicode) { return g_unichar_iswide (unicode); }
+static hb_codepoint_t
+hb_glib_get_mirroring (hb_unicode_funcs_t *ufuncs,
+                       hb_codepoint_t      unicode,
+                       void               *user_data)
+{
+  g_unichar_get_mirror_char (unicode, &unicode);
+  return unicode;
+}
+
+static hb_unicode_general_category_t
+hb_glib_get_general_category (hb_unicode_funcs_t *ufuncs,
+                              hb_codepoint_t      unicode,
+                              void               *user_data)
+
+{
+  return g_unichar_type (unicode);
+}
+
+static hb_script_t
+hb_glib_get_script (hb_unicode_funcs_t *ufuncs,
+                    hb_codepoint_t      unicode,
+                    void               *user_data)
+{
+  GUnicodeScript script = g_unichar_get_script (unicode);
+  switch (script)
+  {
+#define MATCH_SCRIPT(C) case G_UNICODE_SCRIPT_##C: return HB_SCRIPT_##C
+#define MATCH_SCRIPT2(C1, C2) case G_UNICODE_SCRIPT_##C1: return HB_SCRIPT_##C2
+
+  MATCH_SCRIPT2(INVALID_CODE, INVALID);
+
+  MATCH_SCRIPT (COMMON);             /* Zyyy */
+  MATCH_SCRIPT (INHERITED);          /* Qaai */
+  MATCH_SCRIPT (ARABIC);             /* Arab */
+  MATCH_SCRIPT (ARMENIAN);           /* Armn */
+  MATCH_SCRIPT (BENGALI);            /* Beng */
+  MATCH_SCRIPT (BOPOMOFO);           /* Bopo */
+  MATCH_SCRIPT (CHEROKEE);           /* Cher */
+  MATCH_SCRIPT (COPTIC);             /* Qaac */
+  MATCH_SCRIPT (CYRILLIC);           /* Cyrl (Cyrs) */
+  MATCH_SCRIPT (DESERET);            /* Dsrt */
+  MATCH_SCRIPT (DEVANAGARI);         /* Deva */
+  MATCH_SCRIPT (ETHIOPIC);           /* Ethi */
+  MATCH_SCRIPT (GEORGIAN);           /* Geor (Geon); Geoa) */
+  MATCH_SCRIPT (GOTHIC);             /* Goth */
+  MATCH_SCRIPT (GREEK);              /* Grek */
+  MATCH_SCRIPT (GUJARATI);           /* Gujr */
+  MATCH_SCRIPT (GURMUKHI);           /* Guru */
+  MATCH_SCRIPT (HAN);                /* Hani */
+  MATCH_SCRIPT (HANGUL);             /* Hang */
+  MATCH_SCRIPT (HEBREW);             /* Hebr */
+  MATCH_SCRIPT (HIRAGANA);           /* Hira */
+  MATCH_SCRIPT (KANNADA);            /* Knda */
+  MATCH_SCRIPT (KATAKANA);           /* Kana */
+  MATCH_SCRIPT (KHMER);              /* Khmr */
+  MATCH_SCRIPT (LAO);                /* Laoo */
+  MATCH_SCRIPT (LATIN);              /* Latn (Latf); Latg) */
+  MATCH_SCRIPT (MALAYALAM);          /* Mlym */
+  MATCH_SCRIPT (MONGOLIAN);          /* Mong */
+  MATCH_SCRIPT (MYANMAR);            /* Mymr */
+  MATCH_SCRIPT (OGHAM);              /* Ogam */
+  MATCH_SCRIPT (OLD_ITALIC);         /* Ital */
+  MATCH_SCRIPT (ORIYA);              /* Orya */
+  MATCH_SCRIPT (RUNIC);              /* Runr */
+  MATCH_SCRIPT (SINHALA);            /* Sinh */
+  MATCH_SCRIPT (SYRIAC);             /* Syrc (Syrj, Syrn); Syre) */
+  MATCH_SCRIPT (TAMIL);              /* Taml */
+  MATCH_SCRIPT (TELUGU);             /* Telu */
+  MATCH_SCRIPT (THAANA);             /* Thaa */
+  MATCH_SCRIPT (THAI);               /* Thai */
+  MATCH_SCRIPT (TIBETAN);            /* Tibt */
+  MATCH_SCRIPT (CANADIAN_ABORIGINAL);/* Cans */
+  MATCH_SCRIPT (YI);                 /* Yiii */
+  MATCH_SCRIPT (TAGALOG);            /* Tglg */
+  MATCH_SCRIPT (HANUNOO);            /* Hano */
+  MATCH_SCRIPT (BUHID);              /* Buhd */
+  MATCH_SCRIPT (TAGBANWA);           /* Tagb */
+
+  /* Unicode-4.0 additions */
+  MATCH_SCRIPT (BRAILLE);            /* Brai */
+  MATCH_SCRIPT (CYPRIOT);            /* Cprt */
+  MATCH_SCRIPT (LIMBU);              /* Limb */
+  MATCH_SCRIPT (OSMANYA);            /* Osma */
+  MATCH_SCRIPT (SHAVIAN);            /* Shaw */
+  MATCH_SCRIPT (LINEAR_B);           /* Linb */
+  MATCH_SCRIPT (TAI_LE);             /* Tale */
+  MATCH_SCRIPT (UGARITIC);           /* Ugar */
+
+  /* Unicode-4.1 additions */
+  MATCH_SCRIPT (NEW_TAI_LUE);        /* Talu */
+  MATCH_SCRIPT (BUGINESE);           /* Bugi */
+  MATCH_SCRIPT (GLAGOLITIC);         /* Glag */
+  MATCH_SCRIPT (TIFINAGH);           /* Tfng */
+  MATCH_SCRIPT (SYLOTI_NAGRI);       /* Sylo */
+  MATCH_SCRIPT (OLD_PERSIAN);        /* Xpeo */
+  MATCH_SCRIPT (KHAROSHTHI);         /* Khar */
+
+  /* Unicode-5.0 additions */
+  MATCH_SCRIPT (UNKNOWN);            /* Zzzz */
+  MATCH_SCRIPT (BALINESE);           /* Bali */
+  MATCH_SCRIPT (CUNEIFORM);          /* Xsux */
+  MATCH_SCRIPT (PHOENICIAN);         /* Phnx */
+  MATCH_SCRIPT (PHAGS_PA);           /* Phag */
+  MATCH_SCRIPT (NKO);                /* Nkoo */
 
+  /* Unicode-5.1 additions */
+  MATCH_SCRIPT (KAYAH_LI);           /* Kali */
+  MATCH_SCRIPT (LEPCHA);             /* Lepc */
+  MATCH_SCRIPT (REJANG);             /* Rjng */
+  MATCH_SCRIPT (SUNDANESE);          /* Sund */
+  MATCH_SCRIPT (SAURASHTRA);         /* Saur */
+  MATCH_SCRIPT (CHAM);               /* Cham */
+  MATCH_SCRIPT (OL_CHIKI);           /* Olck */
+  MATCH_SCRIPT (VAI);                /* Vaii */
+  MATCH_SCRIPT (CARIAN);             /* Cari */
+  MATCH_SCRIPT (LYCIAN);             /* Lyci */
+  MATCH_SCRIPT (LYDIAN);             /* Lydi */
+
+  /* Unicode-5.2 additions */
+#if GLIB_CHECK_VERSION(2,26,0)
+  MATCH_SCRIPT (AVESTAN);                /* Avst */
+  MATCH_SCRIPT (BAMUM);                  /* Bamu */
+  MATCH_SCRIPT (EGYPTIAN_HIEROGLYPHS);   /* Egyp */
+  MATCH_SCRIPT (IMPERIAL_ARAMAIC);       /* Armi */
+  MATCH_SCRIPT (INSCRIPTIONAL_PAHLAVI);  /* Phli */
+  MATCH_SCRIPT (INSCRIPTIONAL_PARTHIAN); /* Prti */
+  MATCH_SCRIPT (JAVANESE);               /* Java */
+  MATCH_SCRIPT (KAITHI);                 /* Kthi */
+  MATCH_SCRIPT (TAI_THAM);               /* Lana */
+  MATCH_SCRIPT (LISU);                   /* Lisu */
+  MATCH_SCRIPT (MEETEI_MAYEK);           /* Mtei */
+  MATCH_SCRIPT (OLD_SOUTH_ARABIAN);      /* Sarb */
+#if GLIB_CHECK_VERSION(2,28,0)
+  MATCH_SCRIPT (OLD_TURKIC);             /* Orkh */
+#else
+  MATCH_SCRIPT2(OLD_TURKISH, OLD_TURKIC);/* Orkh */
+#endif
+  MATCH_SCRIPT (SAMARITAN);              /* Samr */
+  MATCH_SCRIPT (TAI_VIET);               /* Tavt */
+#endif
+
+  /* Unicode-6.0 additions */
+#if GLIB_CHECK_VERSION(2,28,0)
+  MATCH_SCRIPT (BATAK);                  /* Batk */
+  MATCH_SCRIPT (BRAHMI);                 /* Brah */
+  MATCH_SCRIPT (MANDAIC);                /* Mand */
+#endif
+
+#undef MATCH_SCRIPT
+#undef MATCH_SCRIPT2
+  }
+
+  return HB_SCRIPT_UNKNOWN;
+}
+
+static unsigned int
+hb_glib_get_combining_class (hb_unicode_funcs_t *ufuncs,
+                             hb_codepoint_t      unicode,
+                             void               *user_data)
+
+{
+  return g_unichar_combining_class (unicode);
+}
+
+static unsigned int
+hb_glib_get_eastasian_width (hb_unicode_funcs_t *ufuncs,
+                             hb_codepoint_t      unicode,
+                             void               *user_data)
+{
+  return g_unichar_iswide (unicode) ? 2 : 1;
+}
 
 static hb_unicode_funcs_t glib_ufuncs = {
   HB_REFERENCE_COUNT_INVALID, /* ref_count */
+  NULL,
   TRUE, /* immutable */
   {
-    hb_glib_get_general_category,
-    hb_glib_get_combining_class,
-    hb_glib_get_mirroring,
-    hb_glib_get_script,
-    hb_glib_get_eastasian_width
+    hb_glib_get_general_category, NULL, NULL,
+    hb_glib_get_combining_class, NULL, NULL,
+    hb_glib_get_mirroring, NULL, NULL,
+    hb_glib_get_script, NULL, NULL,
+    hb_glib_get_eastasian_width, NULL, NULL
   }
 };
 
index 0ebc9c5..6bc3339 100644 (file)
 HB_BEGIN_DECLS
 
 
-static hb_codepoint_t hb_icu_get_mirroring (hb_codepoint_t unicode) { return u_charMirror(unicode); }
-static unsigned int hb_icu_get_combining_class (hb_codepoint_t unicode) { return u_getCombiningClass (unicode); }
+static hb_codepoint_t
+hb_icu_get_mirroring (hb_unicode_funcs_t *ufuncs,
+                     hb_codepoint_t      unicode,
+                     void               *user_data)
+{
+  return u_charMirror(unicode);
+}
+
+static unsigned int
+hb_icu_get_combining_class (hb_unicode_funcs_t *ufuncs,
+                           hb_codepoint_t      unicode,
+                           void               *user_data)
+
+{
+  return u_getCombiningClass (unicode);
+}
 
 static unsigned int
-hb_icu_get_eastasian_width (hb_codepoint_t unicode)
+hb_icu_get_eastasian_width (hb_unicode_funcs_t *ufuncs,
+                           hb_codepoint_t      unicode,
+                           void               *user_data)
 {
   switch (u_getIntPropertyValue(unicode, UCHAR_EAST_ASIAN_WIDTH))
   {
@@ -59,7 +75,9 @@ hb_icu_get_eastasian_width (hb_codepoint_t unicode)
 }
 
 static hb_unicode_general_category_t
-hb_icu_get_general_category (hb_codepoint_t unicode)
+hb_icu_get_general_category (hb_unicode_funcs_t *ufuncs,
+                            hb_codepoint_t      unicode,
+                            void               *user_data)
 {
   switch (u_getIntPropertyValue(unicode, UCHAR_GENERAL_CATEGORY))
   {
@@ -108,7 +126,9 @@ hb_icu_get_general_category (hb_codepoint_t unicode)
 }
 
 static hb_script_t
-hb_icu_get_script (hb_codepoint_t unicode)
+hb_icu_get_script (hb_unicode_funcs_t *ufuncs,
+                  hb_codepoint_t      unicode,
+                  void               *user_data)
 {
   UErrorCode status = U_ZERO_ERROR;
   UScriptCode scriptCode = uscript_getScript(unicode, &status);
@@ -234,19 +254,24 @@ hb_icu_get_script (hb_codepoint_t unicode)
   MATCH_SCRIPT (BRAHMI);                 /* Brah */
   MATCH_SCRIPT2(MANDAEAN, MANDAIC);      /* Mand */
 
+#undef CHECK_ICU_VERSION
+#undef MATCH_SCRIPT
+#undef MATCH_SCRIPT2
   }
+
   return HB_SCRIPT_UNKNOWN;
 }
 
 static hb_unicode_funcs_t icu_ufuncs = {
   HB_REFERENCE_COUNT_INVALID, /* ref_count */
+  NULL,
   TRUE, /* immutable */
   {
-    hb_icu_get_general_category,
-    hb_icu_get_combining_class,
-    hb_icu_get_mirroring,
-    hb_icu_get_script,
-    hb_icu_get_eastasian_width
+    hb_icu_get_general_category, NULL, NULL,
+    hb_icu_get_combining_class, NULL, NULL,
+    hb_icu_get_mirroring, NULL, NULL,
+    hb_icu_get_script, NULL, NULL,
+    hb_icu_get_eastasian_width, NULL, NULL
   }
 };
 
index 2364d3c..2ffb076 100644 (file)
@@ -149,8 +149,10 @@ hb_set_unicode_props (hb_ot_shape_context_t *c)
 
   unsigned int count = c->buffer->len;
   for (unsigned int i = 1; i < count; i++) {
-    info[i].general_category() = get_general_category (info[i].codepoint);
-    info[i].combining_class() = get_combining_class (info[i].codepoint);
+    info[i].general_category() = get_general_category (c->buffer->unicode, info[i].codepoint,
+                                                       c->buffer->unicode->v.get_general_category_data);
+    info[i].combining_class() = get_combining_class (c->buffer->unicode, info[i].codepoint,
+                                                     c->buffer->unicode->v.get_combining_class_data);
   }
 }
 
@@ -200,7 +202,8 @@ hb_mirror_chars (hb_ot_shape_context_t *c)
 
   unsigned int count = c->buffer->len;
   for (unsigned int i = 0; i < count; i++) {
-    hb_codepoint_t codepoint = get_mirroring (c->buffer->info[i].codepoint);
+    hb_codepoint_t codepoint = get_mirroring (c->buffer->unicode, c->buffer->info[i].codepoint,
+                                              c->buffer->unicode->v.get_mirroring_data);
     if (likely (codepoint == c->buffer->info[i].codepoint))
       c->buffer->info[i].mask |= rtlm_mask; /* XXX this should be moved to before setting user-feature masks */
     else
index 368abfc..9d9bf25 100644 (file)
@@ -77,7 +77,7 @@ hb_shape (hb_font_t          *font,
     hb_unicode_get_script_func_t get_script = buffer->unicode->v.get_script;
     unsigned int count = buffer->len;
     for (unsigned int i = 0; i < count; i++) {
-      hb_script_t script = get_script (buffer->info[i].codepoint);
+      hb_script_t script = get_script (buffer->unicode, buffer->info[i].codepoint, buffer->unicode->v.get_script_data);
       if (likely (script != HB_SCRIPT_COMMON &&
                  script != HB_SCRIPT_INHERITED &&
                  script != HB_SCRIPT_UNKNOWN)) {
index 52d1b9b..456d8de 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2009  Red Hat, Inc.
+ * Copyright © 2011 Codethink Limited
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
@@ -22,6 +23,7 @@
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Red Hat Author(s): Behdad Esfahbod
+ * Codethink Author(s): Ryan Lortie
  */
 
 #ifndef HB_UNICODE_PRIVATE_H
@@ -40,15 +42,30 @@ HB_BEGIN_DECLS
 
 struct _hb_unicode_funcs_t {
   hb_reference_count_t ref_count;
+  hb_unicode_funcs_t *parent;
 
   hb_bool_t immutable;
 
   struct {
     hb_unicode_get_general_category_func_t     get_general_category;
+    void                                       *get_general_category_data;
+    hb_destroy_func_t                           get_general_category_destroy;
+
     hb_unicode_get_combining_class_func_t      get_combining_class;
+    void                                       *get_combining_class_data;
+    hb_destroy_func_t                           get_combining_class_destroy;
+
     hb_unicode_get_mirroring_func_t            get_mirroring;
+    void                                       *get_mirroring_data;
+    hb_destroy_func_t                           get_mirroring_destroy;
+
     hb_unicode_get_script_func_t               get_script;
+    void                                       *get_script_data;
+    hb_destroy_func_t                           get_script_destroy;
+
     hb_unicode_get_eastasian_width_func_t      get_eastasian_width;
+    void                                       *get_eastasian_width_data;
+    hb_destroy_func_t                           get_eastasian_width_destroy;
   } v;
 };
 
index 9bb3524..e047ef7 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2009  Red Hat, Inc.
+ * Copyright © 2011 Codethink Limited
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
@@ -22,6 +23,7 @@
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Red Hat Author(s): Behdad Esfahbod
+ * Codethink Author(s): Ryan Lortie
  */
 
 #include "hb-private.h"
@@ -35,33 +37,86 @@ HB_BEGIN_DECLS
  * hb_unicode_funcs_t
  */
 
-static hb_codepoint_t hb_unicode_get_mirroring_nil (hb_codepoint_t unicode) { return unicode; }
-static hb_unicode_general_category_t hb_unicode_get_general_category_nil (hb_codepoint_t unicode HB_UNUSED) { return HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER; }
-static hb_script_t hb_unicode_get_script_nil (hb_codepoint_t unicode HB_UNUSED) { return HB_SCRIPT_UNKNOWN; }
-static unsigned int hb_unicode_get_combining_class_nil (hb_codepoint_t unicode HB_UNUSED) { return 0; }
-static unsigned int hb_unicode_get_eastasian_width_nil (hb_codepoint_t unicode HB_UNUSED) { return 1; }
+static hb_codepoint_t
+hb_unicode_get_mirroring_nil (hb_unicode_funcs_t *ufuncs    HB_UNUSED,
+                              hb_codepoint_t      unicode   HB_UNUSED,
+                              void               *user_data HB_UNUSED)
+{
+  return unicode;
+}
+
+static hb_unicode_general_category_t
+hb_unicode_get_general_category_nil (hb_unicode_funcs_t *ufuncs    HB_UNUSED,
+                                     hb_codepoint_t      unicode   HB_UNUSED,
+                                     void               *user_data HB_UNUSED)
+{
+  return HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER;
+}
+
+static hb_script_t
+hb_unicode_get_script_nil (hb_unicode_funcs_t *ufuncs    HB_UNUSED,
+                           hb_codepoint_t      unicode   HB_UNUSED,
+                           void               *user_data HB_UNUSED)
+{
+  return HB_SCRIPT_UNKNOWN;
+}
+
+static unsigned int
+hb_unicode_get_combining_class_nil (hb_unicode_funcs_t *ufuncs    HB_UNUSED,
+                                    hb_codepoint_t      unicode   HB_UNUSED,
+                                    void               *user_data HB_UNUSED)
+{
+  return 0;
+}
+
+static unsigned int
+hb_unicode_get_eastasian_width_nil (hb_unicode_funcs_t *ufuncs    HB_UNUSED,
+                                    hb_codepoint_t      unicode   HB_UNUSED,
+                                    void               *user_data HB_UNUSED)
+{
+  return 1;
+}
 
 hb_unicode_funcs_t _hb_unicode_funcs_nil = {
   HB_REFERENCE_COUNT_INVALID, /* ref_count */
+  NULL, /* parent */
   TRUE, /* immutable */
   {
-    hb_unicode_get_general_category_nil,
-    hb_unicode_get_combining_class_nil,
-    hb_unicode_get_mirroring_nil,
-    hb_unicode_get_script_nil,
-    hb_unicode_get_eastasian_width_nil
+    hb_unicode_get_general_category_nil, NULL, NULL,
+    hb_unicode_get_combining_class_nil, NULL, NULL,
+    hb_unicode_get_mirroring_nil, NULL, NULL,
+    hb_unicode_get_script_nil, NULL, NULL,
+    hb_unicode_get_eastasian_width_nil, NULL, NULL
   }
 };
 
 hb_unicode_funcs_t *
-hb_unicode_funcs_create (void)
+hb_unicode_funcs_create (hb_unicode_funcs_t *parent)
 {
   hb_unicode_funcs_t *ufuncs;
 
   if (!HB_OBJECT_DO_CREATE (hb_unicode_funcs_t, ufuncs))
     return &_hb_unicode_funcs_nil;
 
-  ufuncs->v = _hb_unicode_funcs_nil.v;
+  if (parent != NULL) {
+    ufuncs->parent = hb_unicode_funcs_reference (parent);
+    hb_unicode_funcs_make_immutable (parent);
+    ufuncs->v = parent->v;
+
+    /* Clear out the destroy notifies from our parent.
+     *
+     * We don't want to destroy the user_data twice and since we hold a
+     * reference on our parent then we know that the user_data will
+     * survive for at least as long as we do anyway.
+     */
+    ufuncs->v.get_general_category_destroy = NULL;
+    ufuncs->v.get_combining_class_destroy = NULL;
+    ufuncs->v.get_mirroring_destroy = NULL;
+    ufuncs->v.get_script_destroy = NULL;
+    ufuncs->v.get_eastasian_width_destroy = NULL;
+  } else {
+    ufuncs->v = _hb_unicode_funcs_nil.v;
+  }
 
   return ufuncs;
 }
@@ -83,20 +138,31 @@ hb_unicode_funcs_destroy (hb_unicode_funcs_t *ufuncs)
 {
   HB_OBJECT_DO_DESTROY (ufuncs);
 
+  if (ufuncs->parent != NULL)
+    hb_unicode_funcs_destroy (ufuncs->parent);
+
+  if (ufuncs->v.get_general_category_destroy != NULL)
+    ufuncs->v.get_general_category_destroy (ufuncs->v.get_general_category_data);
+
+  if (ufuncs->v.get_combining_class_destroy != NULL)
+    ufuncs->v.get_combining_class_destroy (ufuncs->v.get_combining_class_data);
+
+  if (ufuncs->v.get_mirroring_destroy != NULL)
+    ufuncs->v.get_mirroring_destroy (ufuncs->v.get_mirroring_data);
+
+  if (ufuncs->v.get_script_destroy != NULL)
+    ufuncs->v.get_script_destroy (ufuncs->v.get_script_data);
+
+  if (ufuncs->v.get_eastasian_width_destroy != NULL)
+    ufuncs->v.get_eastasian_width_destroy (ufuncs->v.get_eastasian_width_data);
+
   free (ufuncs);
 }
 
 hb_unicode_funcs_t *
-hb_unicode_funcs_copy (hb_unicode_funcs_t *other_ufuncs)
+hb_unicode_funcs_get_parent (hb_unicode_funcs_t *ufuncs)
 {
-  hb_unicode_funcs_t *ufuncs;
-
-  if (!HB_OBJECT_DO_CREATE (hb_unicode_funcs_t, ufuncs))
-    return &_hb_unicode_funcs_nil;
-
-  ufuncs->v = other_ufuncs->v;
-
-  return ufuncs;
+  return ufuncs->parent;
 }
 
 void
@@ -115,122 +181,75 @@ hb_unicode_funcs_is_immutable (hb_unicode_funcs_t *ufuncs)
 }
 
 
-void
-hb_unicode_funcs_set_mirroring_func (hb_unicode_funcs_t *ufuncs,
-                                    hb_unicode_get_mirroring_func_t mirroring_func)
-{
-  if (ufuncs->immutable)
-    return;
-
-  ufuncs->v.get_mirroring = mirroring_func ? mirroring_func : hb_unicode_get_mirroring_nil;
+#define SETTER(name) \
+void                                                                           \
+hb_unicode_funcs_set_##name##_func (hb_unicode_funcs_t             *ufuncs,    \
+                                    hb_unicode_get_##name##_func_t  func,      \
+                                    void                           *user_data, \
+                                    hb_destroy_func_t               destroy)   \
+{                                                                              \
+  if (ufuncs->immutable)                                                       \
+    return;                                                                    \
+                                                                               \
+  if (func != NULL) {                                                          \
+    ufuncs->v.get_##name = func;                                               \
+    ufuncs->v.get_##name##_data = user_data;                                   \
+    ufuncs->v.get_##name##_destroy = destroy;                                  \
+  } else if (ufuncs->parent != NULL) {                                         \
+    ufuncs->v.get_##name = ufuncs->parent->v.get_##name;                       \
+    ufuncs->v.get_##name##_data = ufuncs->parent->v.get_##name##_data;;        \
+    ufuncs->v.get_##name##_destroy = NULL;                                     \
+  } else {                                                                     \
+    ufuncs->v.get_##name = hb_unicode_get_##name##_nil;                        \
+    ufuncs->v.get_##name##_data = NULL;                                        \
+    ufuncs->v.get_##name##_destroy = NULL;                                     \
+  }                                                                            \
 }
 
-void
-hb_unicode_funcs_set_general_category_func (hb_unicode_funcs_t *ufuncs,
-                                           hb_unicode_get_general_category_func_t general_category_func)
-{
-  if (ufuncs->immutable)
-    return;
-
-  ufuncs->v.get_general_category = general_category_func ? general_category_func : hb_unicode_get_general_category_nil;
-}
-
-void
-hb_unicode_funcs_set_script_func (hb_unicode_funcs_t *ufuncs,
-                                 hb_unicode_get_script_func_t script_func)
-{
-  if (ufuncs->immutable)
-    return;
-
-  ufuncs->v.get_script = script_func ? script_func : hb_unicode_get_script_nil;
-}
-
-void
-hb_unicode_funcs_set_combining_class_func (hb_unicode_funcs_t *ufuncs,
-                                          hb_unicode_get_combining_class_func_t combining_class_func)
-{
-  if (ufuncs->immutable)
-    return;
-
-  ufuncs->v.get_combining_class = combining_class_func ? combining_class_func : hb_unicode_get_combining_class_nil;
-}
-
-void
-hb_unicode_funcs_set_eastasian_width_func (hb_unicode_funcs_t *ufuncs,
-                                          hb_unicode_get_eastasian_width_func_t eastasian_width_func)
-{
-  if (ufuncs->immutable)
-    return;
-
-  ufuncs->v.get_eastasian_width = eastasian_width_func ? eastasian_width_func : hb_unicode_get_eastasian_width_nil;
-}
-
-
-hb_unicode_get_mirroring_func_t
-hb_unicode_funcs_get_mirroring_func (hb_unicode_funcs_t *ufuncs)
-{
-  return ufuncs->v.get_mirroring;
-}
-
-hb_unicode_get_general_category_func_t
-hb_unicode_funcs_get_general_category_func (hb_unicode_funcs_t *ufuncs)
-{
-  return ufuncs->v.get_general_category;
-}
-
-hb_unicode_get_script_func_t
-hb_unicode_funcs_get_script_func (hb_unicode_funcs_t *ufuncs)
-{
-  return ufuncs->v.get_script;
-}
-
-hb_unicode_get_combining_class_func_t
-hb_unicode_funcs_get_combining_class_func (hb_unicode_funcs_t *ufuncs)
-{
-  return ufuncs->v.get_combining_class;
-}
-
-hb_unicode_get_eastasian_width_func_t
-hb_unicode_funcs_get_eastasian_width_func (hb_unicode_funcs_t *ufuncs)
-{
-  return ufuncs->v.get_eastasian_width;
-}
-
-
+SETTER(mirroring)
+SETTER(general_category)
+SETTER(script)
+SETTER(combining_class)
+SETTER(eastasian_width)
 
 hb_codepoint_t
 hb_unicode_get_mirroring (hb_unicode_funcs_t *ufuncs,
                          hb_codepoint_t unicode)
 {
-  return ufuncs->v.get_mirroring (unicode);
+  return ufuncs->v.get_mirroring (ufuncs, unicode,
+                                 ufuncs->v.get_mirroring_data);
 }
 
 hb_unicode_general_category_t
 hb_unicode_get_general_category (hb_unicode_funcs_t *ufuncs,
                                 hb_codepoint_t unicode)
 {
-  return ufuncs->v.get_general_category (unicode);
+  return ufuncs->v.get_general_category (ufuncs, unicode,
+                                        ufuncs->v.get_general_category_data);
 }
 
 hb_script_t
 hb_unicode_get_script (hb_unicode_funcs_t *ufuncs,
                       hb_codepoint_t unicode)
 {
-  return ufuncs->v.get_script (unicode);
+  return ufuncs->v.get_script (ufuncs, unicode,
+                              ufuncs->v.get_script_data);
 }
 
 unsigned int
 hb_unicode_get_combining_class (hb_unicode_funcs_t *ufuncs,
                                hb_codepoint_t unicode)
 {
-  return ufuncs->v.get_combining_class (unicode);
+  return ufuncs->v.get_combining_class (ufuncs, unicode,
+                                       ufuncs->v.get_combining_class_data);
 }
 
 unsigned int
 hb_unicode_get_eastasian_width (hb_unicode_funcs_t *ufuncs,
                                hb_codepoint_t unicode)
 {
-  return ufuncs->v.get_eastasian_width (unicode);
+  return ufuncs->v.get_eastasian_width (ufuncs, unicode,
+                                       ufuncs->v.get_eastasian_width_data);
 }
 
 
index fb8fe97..792ad54 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2009  Red Hat, Inc.
+ * Copyright © 2011 Codethink Limited
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
@@ -22,6 +23,7 @@
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Red Hat Author(s): Behdad Esfahbod
+ * Codethink Author(s): Ryan Lortie
  */
 
 #ifndef HB_UNICODE_H
@@ -39,7 +41,7 @@ HB_BEGIN_DECLS
 typedef struct _hb_unicode_funcs_t hb_unicode_funcs_t;
 
 hb_unicode_funcs_t *
-hb_unicode_funcs_create (void);
+hb_unicode_funcs_create (hb_unicode_funcs_t *parent_funcs);
 
 hb_unicode_funcs_t *
 hb_unicode_funcs_reference (hb_unicode_funcs_t *ufuncs);
@@ -51,7 +53,7 @@ void
 hb_unicode_funcs_destroy (hb_unicode_funcs_t *ufuncs);
 
 hb_unicode_funcs_t *
-hb_unicode_funcs_copy (hb_unicode_funcs_t *ufuncs);
+hb_unicode_funcs_get_parent (hb_unicode_funcs_t *ufuncs);
 
 void
 hb_unicode_funcs_make_immutable (hb_unicode_funcs_t *ufuncs);
@@ -63,57 +65,55 @@ hb_unicode_funcs_is_immutable (hb_unicode_funcs_t *ufuncs);
  * funcs
  */
 
-
 /* typedefs */
 
-typedef hb_codepoint_t (*hb_unicode_get_mirroring_func_t) (hb_codepoint_t unicode);
-typedef hb_unicode_general_category_t (*hb_unicode_get_general_category_func_t) (hb_codepoint_t unicode);
-typedef hb_script_t (*hb_unicode_get_script_func_t) (hb_codepoint_t unicode);
-typedef unsigned int (*hb_unicode_get_combining_class_func_t) (hb_codepoint_t unicode);
-typedef unsigned int (*hb_unicode_get_eastasian_width_func_t) (hb_codepoint_t unicode);
-
+typedef hb_codepoint_t                (*hb_unicode_get_mirroring_func_t)        (hb_unicode_funcs_t *ufuncs,
+                                                                                 hb_codepoint_t      unicode,
+                                                                                 void               *user_data);
+typedef hb_unicode_general_category_t (*hb_unicode_get_general_category_func_t) (hb_unicode_funcs_t *ufuncs,
+                                                                                 hb_codepoint_t      unicode,
+                                                                                 void               *user_data);
+typedef hb_script_t                   (*hb_unicode_get_script_func_t)           (hb_unicode_funcs_t *ufuncs,
+                                                                                 hb_codepoint_t      unicode,
+                                                                                 void               *user_data);
+typedef unsigned int                  (*hb_unicode_get_combining_class_func_t)  (hb_unicode_funcs_t *ufuncs,
+                                                                                 hb_codepoint_t      unicode,
+                                                                                 void               *user_data);
+typedef unsigned int                  (*hb_unicode_get_eastasian_width_func_t)  (hb_unicode_funcs_t *ufuncs,
+                                                                                 hb_codepoint_t      unicode,
+                                                                                 void               *user_data);
 
 /* setters */
 
 void
 hb_unicode_funcs_set_mirroring_func (hb_unicode_funcs_t *ufuncs,
-                                    hb_unicode_get_mirroring_func_t mirroring_func);
+                                    hb_unicode_get_mirroring_func_t mirroring_func,
+                                     void *user_data,
+                                     hb_destroy_func_t destroy);
 
 void
 hb_unicode_funcs_set_general_category_func (hb_unicode_funcs_t *ufuncs,
-                                           hb_unicode_get_general_category_func_t general_category_func);
+                                           hb_unicode_get_general_category_func_t general_category_func,
+                                            void *user_data,
+                                            hb_destroy_func_t destroy);
 
 void
 hb_unicode_funcs_set_script_func (hb_unicode_funcs_t *ufuncs,
-                                 hb_unicode_get_script_func_t script_func);
+                                 hb_unicode_get_script_func_t script_func,
+                                  void *user_data,
+                                  hb_destroy_func_t destroy);
 
 void
 hb_unicode_funcs_set_combining_class_func (hb_unicode_funcs_t *ufuncs,
-                                          hb_unicode_get_combining_class_func_t combining_class_func);
+                                          hb_unicode_get_combining_class_func_t combining_class_func,
+                                           void *user_data,
+                                           hb_destroy_func_t destroy);
 
 void
 hb_unicode_funcs_set_eastasian_width_func (hb_unicode_funcs_t *ufuncs,
-                                          hb_unicode_get_eastasian_width_func_t eastasian_width_func);
-
-
-/* getters */
-
-/* These never return NULL.  Return fallback defaults instead. */
-
-hb_unicode_get_mirroring_func_t
-hb_unicode_funcs_get_mirroring_func (hb_unicode_funcs_t *ufuncs);
-
-hb_unicode_get_general_category_func_t
-hb_unicode_funcs_get_general_category_func (hb_unicode_funcs_t *ufuncs);
-
-hb_unicode_get_script_func_t
-hb_unicode_funcs_get_script_func (hb_unicode_funcs_t *ufuncs);
-
-hb_unicode_get_combining_class_func_t
-hb_unicode_funcs_get_combining_class_func (hb_unicode_funcs_t *ufuncs);
-
-hb_unicode_get_eastasian_width_func_t
-hb_unicode_funcs_get_eastasian_width_func (hb_unicode_funcs_t *ufuncs);
+                                          hb_unicode_get_eastasian_width_func_t eastasian_width_func,
+                                           void *user_data,
+                                           hb_destroy_func_t destroy);
 
 
 /* accessors */
index 2056b39..37cfed2 100644 (file)
@@ -11,6 +11,7 @@ check_PROGRAMS = $(TEST_PROGS)
 TEST_PROGS += \
        test-buffer \
        test-types \
+       test-unicode \
        $(NULL)
 
 
diff --git a/test/test-unicode.c b/test/test-unicode.c
new file mode 100644 (file)
index 0000000..f610c1d
--- /dev/null
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2011 Codethink Limited
+ *
+ *  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.
+ *
+ * Codethink Author(s): Ryan Lortie
+ */
+
+#include "hb-test.h"
+
+/* This file tests the unicode virtual functions interface */
+
+static void
+test_nil (void)
+{
+  hb_unicode_funcs_t *uf = hb_unicode_funcs_create (NULL);
+
+  g_assert_cmpint (hb_unicode_get_script (uf, 'd'), ==, HB_SCRIPT_UNKNOWN);
+
+  g_assert_cmpint (hb_unicode_funcs_get_reference_count (uf), ==, 1);
+  hb_unicode_funcs_destroy (uf);
+}
+
+static void
+test_glib (void)
+{
+  hb_unicode_funcs_t *uf = hb_glib_get_unicode_funcs ();
+
+  g_assert_cmpint (hb_unicode_get_script (uf, 'd'), ==, HB_SCRIPT_LATIN);
+
+  g_assert_cmpint (hb_unicode_funcs_get_reference_count (uf), ==, 0);
+  hb_unicode_funcs_destroy (uf);
+  g_assert_cmpint (hb_unicode_funcs_get_reference_count (uf), ==, 0);
+}
+
+static gboolean freed0, freed1;
+static int unique_pointer0[1];
+static int unique_pointer1[1];
+static void free_up (void *up)
+{
+  if (up == unique_pointer0) {
+    g_assert (!freed0);
+    freed0 = TRUE;
+  } else if (up == unique_pointer1) {
+    g_assert (!freed1);
+    freed1 = TRUE;
+  } else {
+    g_assert_not_reached ();
+  }
+}
+
+static hb_script_t
+simple_get_script (hb_unicode_funcs_t *ufuncs,
+                   hb_codepoint_t      codepoint,
+                   void               *user_data)
+{
+  g_assert (hb_unicode_funcs_get_parent (ufuncs) == NULL);
+  g_assert (unique_pointer0 == user_data);
+
+  if ('a' <= codepoint && codepoint <= 'z')
+    return HB_SCRIPT_LATIN;
+  else
+    return HB_SCRIPT_UNKNOWN;
+}
+
+static void
+test_custom (void)
+{
+  hb_unicode_funcs_t *uf = hb_unicode_funcs_create (NULL);
+
+  hb_unicode_funcs_set_script_func (uf, simple_get_script,
+                                    unique_pointer0, free_up);
+
+  g_assert_cmpint (hb_unicode_get_script (uf, 'a'), ==, HB_SCRIPT_LATIN);
+  g_assert_cmpint (hb_unicode_get_script (uf, '0'), ==, HB_SCRIPT_UNKNOWN);
+
+  g_assert_cmpint (hb_unicode_funcs_get_reference_count (uf), ==, 1);
+  g_assert (!freed0 && !freed1);
+  hb_unicode_funcs_destroy (uf);
+  g_assert (freed0 && !freed1);
+  freed0 = FALSE;
+}
+
+
+static hb_script_t
+a_is_for_arabic_get_script (hb_unicode_funcs_t *ufuncs,
+                            hb_codepoint_t      codepoint,
+                            void               *user_data)
+{
+  g_assert (hb_unicode_funcs_get_parent (ufuncs) != NULL);
+  g_assert (user_data == unique_pointer1);
+
+  if (codepoint == 'a') {
+    return HB_SCRIPT_ARABIC;
+  } else {
+    hb_unicode_funcs_t *parent = hb_unicode_funcs_get_parent (ufuncs);
+
+    return hb_unicode_get_script (parent, codepoint);
+  }
+}
+
+static void
+test_subclassing_nil (void)
+{
+  hb_unicode_funcs_t *uf = hb_unicode_funcs_create (NULL);
+  g_assert_cmpint (hb_unicode_funcs_get_reference_count (uf), ==, 1);
+  hb_unicode_funcs_t *aa = hb_unicode_funcs_create (uf);
+  g_assert_cmpint (hb_unicode_funcs_get_reference_count (uf), ==, 2);
+  hb_unicode_funcs_destroy (uf);
+  g_assert_cmpint (hb_unicode_funcs_get_reference_count (uf), ==, 1);
+
+  hb_unicode_funcs_set_script_func (aa, a_is_for_arabic_get_script,
+                                    unique_pointer1, free_up);
+
+  g_assert_cmpint (hb_unicode_get_script (aa, 'a'), ==, HB_SCRIPT_ARABIC);
+  g_assert_cmpint (hb_unicode_get_script (aa, 'b'), ==, HB_SCRIPT_UNKNOWN);
+
+
+  g_assert_cmpint (hb_unicode_funcs_get_reference_count (aa), ==, 1);
+  g_assert_cmpint (hb_unicode_funcs_get_reference_count (uf), ==, 1);
+  g_assert (!freed0 && !freed1);
+  hb_unicode_funcs_destroy (aa);
+  g_assert (!freed0 && freed1);
+  freed1 = FALSE;
+}
+
+static void
+test_subclassing_glib (void)
+{
+  hb_unicode_funcs_t *uf = hb_glib_get_unicode_funcs ();
+  g_assert_cmpint (hb_unicode_funcs_get_reference_count (uf), ==, 0);
+  hb_unicode_funcs_t *aa = hb_unicode_funcs_create (uf);
+  g_assert_cmpint (hb_unicode_funcs_get_reference_count (uf), ==, 0);
+  hb_unicode_funcs_destroy (uf);
+  g_assert_cmpint (hb_unicode_funcs_get_reference_count (uf), ==, 0);
+
+  hb_unicode_funcs_set_script_func (aa, a_is_for_arabic_get_script,
+                                    unique_pointer1, free_up);
+
+  g_assert_cmpint (hb_unicode_get_script (aa, 'a'), ==, HB_SCRIPT_ARABIC);
+  g_assert_cmpint (hb_unicode_get_script (aa, 'b'), ==, HB_SCRIPT_LATIN);
+
+  g_assert_cmpint (hb_unicode_funcs_get_reference_count (aa), ==, 1);
+  g_assert (!freed0 && !freed1);
+  hb_unicode_funcs_destroy (aa);
+  g_assert (!freed0 && freed1);
+  freed1 = FALSE;
+}
+
+static void
+test_subclassing_deep (void)
+{
+  hb_unicode_funcs_t *uf = hb_unicode_funcs_create (NULL);
+  g_assert_cmpint (hb_unicode_funcs_get_reference_count (uf), ==, 1);
+
+  hb_unicode_funcs_set_script_func (uf, simple_get_script,
+                                    unique_pointer0, free_up);
+
+  hb_unicode_funcs_t *aa = hb_unicode_funcs_create (uf);
+  g_assert_cmpint (hb_unicode_funcs_get_reference_count (aa), ==, 1);
+  g_assert_cmpint (hb_unicode_funcs_get_reference_count (uf), ==, 2);
+
+  hb_unicode_funcs_destroy (uf);
+  g_assert_cmpint (hb_unicode_funcs_get_reference_count (uf), ==, 1);
+
+  /* make sure the 'uf' didn't get freed, since 'aa' holds a ref */
+  g_assert (!freed0);
+
+  hb_unicode_funcs_set_script_func (aa, a_is_for_arabic_get_script,
+                                    unique_pointer1, free_up);
+
+  g_assert_cmpint (hb_unicode_get_script (aa, 'a'), ==, HB_SCRIPT_ARABIC);
+  g_assert_cmpint (hb_unicode_get_script (aa, 'b'), ==, HB_SCRIPT_LATIN);
+  g_assert_cmpint (hb_unicode_get_script (aa, '0'), ==, HB_SCRIPT_UNKNOWN);
+
+  g_assert_cmpint (hb_unicode_funcs_get_reference_count (aa), ==, 1);
+  g_assert_cmpint (hb_unicode_funcs_get_reference_count (uf), ==, 1);
+  g_assert (!freed0 && !freed1);
+  hb_unicode_funcs_destroy (aa);
+  g_assert (freed0 && freed1);
+  freed0 = freed1 = FALSE;
+}
+
+int
+main (int argc, char **argv)
+{
+  g_test_init (&argc, &argv, NULL);
+
+  g_test_add_func ("/unicode/nil", test_nil);
+  g_test_add_func ("/unicode/glib", test_glib);
+  g_test_add_func ("/unicode/custom", test_custom);
+  g_test_add_func ("/unicode/subclassing/nil", test_subclassing_nil);
+  g_test_add_func ("/unicode/subclassing/glib", test_subclassing_glib);
+  g_test_add_func ("/unicode/subclassing/deep", test_subclassing_deep);
+
+  return g_test_run ();
+}