diff options
Diffstat (limited to 'src/hb-shape.cc')
-rw-r--r-- | src/hb-shape.cc | 394 |
1 files changed, 282 insertions, 112 deletions
diff --git a/src/hb-shape.cc b/src/hb-shape.cc index 163a5bf..4e22c61 100644 --- a/src/hb-shape.cc +++ b/src/hb-shape.cc @@ -1,5 +1,6 @@ /* * Copyright © 2009 Red Hat, Inc. + * Copyright © 2012 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -22,128 +23,279 @@ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. * * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod */ #include "hb-private.hh" -#include "hb-shape.h" - +#include "hb-shaper-private.hh" +#include "hb-shape-plan-private.hh" #include "hb-buffer-private.hh" +#include "hb-font-private.hh" -#ifdef HAVE_GRAPHITE -#include "hb-graphite2-private.hh" -#endif -#ifdef HAVE_UNISCRIBE -# include "hb-uniscribe-private.hh" -#endif -#ifdef HAVE_OT -# include "hb-ot-shape-private.hh" -#endif -#include "hb-fallback-shape-private.hh" - -typedef hb_bool_t (*hb_shape_func_t) (hb_font_t *font, - hb_buffer_t *buffer, - const hb_feature_t *features, - unsigned int num_features); - -#define HB_SHAPER_IMPLEMENT(name) {#name, _hb_##name##_shape} -static const struct hb_shaper_pair_t { - char name[16]; - hb_shape_func_t func; -} all_shapers[] = { - /* v--- Add new shapers in the right place here */ -#ifdef HAVE_GRAPHITE - HB_SHAPER_IMPLEMENT (graphite2), -#endif -#ifdef HAVE_UNISCRIBE - HB_SHAPER_IMPLEMENT (uniscribe), -#endif -#ifdef HAVE_OT - HB_SHAPER_IMPLEMENT (ot), -#endif - HB_SHAPER_IMPLEMENT (fallback) /* should be last */ -}; -#undef HB_SHAPER_IMPLEMENT +static bool +parse_space (const char **pp, const char *end) +{ + while (*pp < end && ISSPACE (**pp)) + (*pp)++; + return true; +} -/* Thread-safe, lock-free, shapers */ +static bool +parse_char (const char **pp, const char *end, char c) +{ + parse_space (pp, end); -static hb_shaper_pair_t *static_shapers; + if (*pp == end || **pp != c) + return false; -static -void free_static_shapers (void) + (*pp)++; + return true; +} + +static bool +parse_uint (const char **pp, const char *end, unsigned int *pv) { - if (unlikely (static_shapers != all_shapers)) - free (static_shapers); + char buf[32]; + unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp)); + strncpy (buf, *pp, len); + buf[len] = '\0'; + + char *p = buf; + char *pend = p; + unsigned int v; + + /* Intentionally use strtol instead of strtoul, such that + * -1 turns into "big number"... */ + errno = 0; + v = strtol (p, &pend, 0); + if (errno || p == pend) + return false; + + *pv = v; + *pp += pend - p; + return true; } -static const hb_shaper_pair_t * -get_shapers (void) +static bool +parse_bool (const char **pp, const char *end, unsigned int *pv) { -retry: - hb_shaper_pair_t *shapers = (hb_shaper_pair_t *) hb_atomic_ptr_get (&static_shapers); + parse_space (pp, end); + + const char *p = *pp; + while (*pp < end && ISALPHA(**pp)) + (*pp)++; + + /* CSS allows on/off as aliases 1/0. */ + if (*pp - p == 2 || 0 == strncmp (p, "on", 2)) + *pv = 1; + else if (*pp - p == 3 || 0 == strncmp (p, "off", 2)) + *pv = 0; + else + return false; + + return true; +} + +static bool +parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feature) +{ + if (parse_char (pp, end, '-')) + feature->value = 0; + else { + parse_char (pp, end, '+'); + feature->value = 1; + } + + return true; +} - if (unlikely (!shapers)) +static bool +parse_feature_tag (const char **pp, const char *end, hb_feature_t *feature) +{ + parse_space (pp, end); + + char quote = 0; + + if (*pp < end && (**pp == '\'' || **pp == '"')) { - char *env = getenv ("HB_SHAPER_LIST"); - if (!env || !*env) { - hb_atomic_ptr_cmpexch (&static_shapers, NULL, (const hb_shaper_pair_t *) all_shapers); - return (const hb_shaper_pair_t *) all_shapers; - } + quote = **pp; + (*pp)++; + } - /* Not found; allocate one. */ - shapers = (hb_shaper_pair_t *) malloc (sizeof (all_shapers)); - if (unlikely (!shapers)) - return (const hb_shaper_pair_t *) all_shapers; - memcpy (shapers, all_shapers, sizeof (all_shapers)); - - /* Reorder shaper list to prefer requested shapers. */ - unsigned int i = 0; - char *end, *p = env; - for (;;) { - end = strchr (p, ','); - if (!end) - end = p + strlen (p); - - for (unsigned int j = i; j < ARRAY_LENGTH (all_shapers); j++) - if (end - p == (int) strlen (shapers[j].name) && - 0 == strncmp (shapers[j].name, p, end - p)) - { - /* Reorder this shaper to position i */ - struct hb_shaper_pair_t t = shapers[j]; - memmove (&shapers[i + 1], &shapers[i], sizeof (shapers[i]) * (j - i)); - shapers[i] = t; - i++; - } - - if (!*end) - break; - else - p = end + 1; - } + const char *p = *pp; + while (*pp < end && ISALNUM(**pp)) + (*pp)++; - if (!hb_atomic_ptr_cmpexch (&static_shapers, NULL, shapers)) { - free (shapers); - goto retry; - } + if (p == *pp || *pp - p > 4) + return false; -#ifdef HAVE_ATEXIT - atexit (free_static_shapers); /* First person registers atexit() callback. */ -#endif + feature->tag = hb_tag_from_string (p, *pp - p); + + if (quote) + { + /* CSS expects exactly four bytes. And we only allow quotations for + * CSS compatibility. So, enforce the length. */ + if (*pp - p != 4) + return false; + if (*pp == end || **pp != quote) + return false; + (*pp)++; + } + + return true; +} + +static bool +parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature) +{ + parse_space (pp, end); + + bool has_start; + + feature->start = 0; + feature->end = (unsigned int) -1; + + if (!parse_char (pp, end, '[')) + return true; + + has_start = parse_uint (pp, end, &feature->start); + + if (parse_char (pp, end, ':')) { + parse_uint (pp, end, &feature->end); + } else { + if (has_start) + feature->end = feature->start + 1; } - return shapers; + return parse_char (pp, end, ']'); +} + +static bool +parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature) +{ + bool had_equal = parse_char (pp, end, '='); + bool had_value = parse_uint (pp, end, &feature->value) || + parse_bool (pp, end, &feature->value); + /* CSS doesn't use equal-sign between tag and value. + * If there was an equal-sign, then there *must* be a value. + * A value without an eqaul-sign is ok, but not required. */ + return !had_equal || had_value; +} + + +static bool +parse_one_feature (const char **pp, const char *end, hb_feature_t *feature) +{ + return parse_feature_value_prefix (pp, end, feature) && + parse_feature_tag (pp, end, feature) && + parse_feature_indices (pp, end, feature) && + parse_feature_value_postfix (pp, end, feature) && + parse_space (pp, end) && + *pp == end; +} + +/** + * hb_feature_from_string: + * @str: (array length=len) (element-type uint8_t): + * @len: + * @feature: (out) (optional): + * + * + * + * Return value: + * + * Since: 1.0 + **/ +hb_bool_t +hb_feature_from_string (const char *str, int len, + hb_feature_t *feature) +{ + hb_feature_t feat; + + if (len < 0) + len = strlen (str); + + if (likely (parse_one_feature (&str, str + len, &feat))) + { + if (feature) + *feature = feat; + return true; + } + + if (feature) + memset (feature, 0, sizeof (*feature)); + return false; +} + +/** + * hb_feature_to_string: + * @feature: + * @buf: (array length=size): + * @size: + * + * + * + * Since: 1.0 + **/ +void +hb_feature_to_string (hb_feature_t *feature, + char *buf, unsigned int size) +{ + if (unlikely (!size)) return; + + char s[128]; + unsigned int len = 0; + if (feature->value == 0) + s[len++] = '-'; + hb_tag_to_string (feature->tag, s + len); + len += 4; + while (len && s[len - 1] == ' ') + len--; + if (feature->start != 0 || feature->end != (unsigned int) -1) + { + s[len++] = '['; + if (feature->start) + len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start)); + if (feature->end != feature->start + 1) { + s[len++] = ':'; + if (feature->end != (unsigned int) -1) + len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end)); + } + s[len++] = ']'; + } + if (feature->value > 1) + { + s[len++] = '='; + len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value)); + } + assert (len < ARRAY_LENGTH (s)); + len = MIN (len, size - 1); + memcpy (buf, s, len); + buf[len] = '\0'; } static const char **static_shaper_list; +#ifdef HB_USE_ATEXIT static void free_static_shaper_list (void) { free (static_shaper_list); } +#endif +/** + * hb_shape_list_shapers: + * + * + * + * Return value: (transfer none): + * + * Since: 1.0 + **/ const char ** hb_shape_list_shapers (void) { @@ -153,15 +305,15 @@ retry: if (unlikely (!shaper_list)) { /* Not found; allocate one. */ - shaper_list = (const char **) calloc (1 + ARRAY_LENGTH (all_shapers), sizeof (const char *)); + shaper_list = (const char **) calloc (1 + HB_SHAPERS_COUNT, sizeof (const char *)); if (unlikely (!shaper_list)) { static const char *nil_shaper_list[] = {NULL}; return nil_shaper_list; } - const hb_shaper_pair_t *shapers = get_shapers (); + const hb_shaper_pair_t *shapers = _hb_shapers_get (); unsigned int i; - for (i = 0; i < ARRAY_LENGTH (all_shapers); i++) + for (i = 0; i < HB_SHAPERS_COUNT; i++) shaper_list[i] = shapers[i].name; shaper_list[i] = NULL; @@ -170,7 +322,7 @@ retry: goto retry; } -#ifdef HAVE_ATEXIT +#ifdef HB_USE_ATEXIT atexit (free_static_shaper_list); /* First person registers atexit() callback. */ #endif } @@ -179,6 +331,20 @@ retry: } +/** + * hb_shape_full: + * @font: a font. + * @buffer: a buffer. + * @features: (array length=num_features): + * @num_features: + * @shaper_list: (array zero-terminated=1): + * + * + * + * Return value: + * + * Since: 1.0 + **/ hb_bool_t hb_shape_full (hb_font_t *font, hb_buffer_t *buffer, @@ -186,27 +352,31 @@ hb_shape_full (hb_font_t *font, unsigned int num_features, const char * const *shaper_list) { - hb_font_make_immutable (font); /* So we can safely cache stuff on it */ + if (unlikely (!buffer->len)) + return true; - if (likely (!shaper_list)) { - const hb_shaper_pair_t *shapers = get_shapers (); - for (unsigned int i = 0; i < ARRAY_LENGTH (all_shapers); i++) - if (likely (shapers[i].func (font, buffer, features, num_features))) - return true; - } else { - while (*shaper_list) { - for (unsigned int i = 0; i < ARRAY_LENGTH (all_shapers); i++) - if (0 == strcmp (*shaper_list, all_shapers[i].name)) { - if (likely (all_shapers[i].func (font, buffer, features, num_features))) - return true; - break; - } - shaper_list++; - } - } - return false; + assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE); + + hb_shape_plan_t *shape_plan = hb_shape_plan_create_cached (font->face, &buffer->props, features, num_features, shaper_list); + hb_bool_t res = hb_shape_plan_execute (shape_plan, font, buffer, features, num_features); + hb_shape_plan_destroy (shape_plan); + + if (res) + buffer->content_type = HB_BUFFER_CONTENT_TYPE_GLYPHS; + return res; } +/** + * hb_shape: + * @font: a font. + * @buffer: a buffer. + * @features: (array length=num_features): + * @num_features: + * + * + * + * Since: 1.0 + **/ void hb_shape (hb_font_t *font, hb_buffer_t *buffer, |