From 2fd0c577e322ccbf762927bc4600b3ea31db4c80 Mon Sep 17 00:00:00 2001 From: Ryan Lortie Date: Wed, 20 Apr 2011 00:19:20 -0400 Subject: [PATCH] [API] unicode: rework virtual functions for subclassing 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 | 186 ++++++++++++++++++++++++++++++++++++-- src/hb-icu.c | 45 +++++++-- src/hb-ot-shape.cc | 9 +- src/hb-shape.cc | 2 +- src/hb-unicode-private.h | 17 ++++ src/hb-unicode.c | 231 +++++++++++++++++++++++++---------------------- src/hb-unicode.h | 68 +++++++------- test/Makefile.am | 1 + test/test-unicode.c | 215 +++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 610 insertions(+), 164 deletions(-) create mode 100644 test/test-unicode.c diff --git a/src/hb-glib.c b/src/hb-glib.c index 56123f3..1905637 100644 --- a/src/hb-glib.c +++ b/src/hb-glib.c @@ -35,22 +35,188 @@ 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 } }; diff --git a/src/hb-icu.c b/src/hb-icu.c index 0ebc9c5..6bc3339 100644 --- a/src/hb-icu.c +++ b/src/hb-icu.c @@ -38,11 +38,27 @@ 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 } }; diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc index 2364d3c..2ffb076 100644 --- a/src/hb-ot-shape.cc +++ b/src/hb-ot-shape.cc @@ -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 diff --git a/src/hb-shape.cc b/src/hb-shape.cc index 368abfc..9d9bf25 100644 --- a/src/hb-shape.cc +++ b/src/hb-shape.cc @@ -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)) { diff --git a/src/hb-unicode-private.h b/src/hb-unicode-private.h index 52d1b9b..456d8de 100644 --- a/src/hb-unicode-private.h +++ b/src/hb-unicode-private.h @@ -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; }; diff --git a/src/hb-unicode.c b/src/hb-unicode.c index 9bb3524..e047ef7 100644 --- a/src/hb-unicode.c +++ b/src/hb-unicode.c @@ -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); } diff --git a/src/hb-unicode.h b/src/hb-unicode.h index fb8fe97..792ad54 100644 --- a/src/hb-unicode.h +++ b/src/hb-unicode.h @@ -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 */ diff --git a/test/Makefile.am b/test/Makefile.am index 2056b39..37cfed2 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -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 index 0000000..f610c1d --- /dev/null +++ b/test/test-unicode.c @@ -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 (); +} -- 2.7.4