diff --git a/TODO b/TODO index 1ca24804c..be3c7c6b8 100644 --- a/TODO +++ b/TODO @@ -41,8 +41,6 @@ API additions - Buffer (de)serialize API ala hb-shape? -- Move feature parsing from util into the library - - Add hb-cairo glue - Add sanitize API (and a cached version, that saves result on blob user-data) @@ -91,3 +89,5 @@ Tests to write: - hb_set_t - hb_cache_t and relatives + +- hb_feature_to/from_string diff --git a/src/hb-common.cc b/src/hb-common.cc index 1301ab2b2..33a514dbf 100644 --- a/src/hb-common.cc +++ b/src/hb-common.cc @@ -58,6 +58,15 @@ hb_tag_from_string (const char *s, int len) return HB_TAG_CHAR4 (tag); } +void +hb_tag_to_string (hb_tag_t tag, char *buf) +{ + buf[0] = (char) (uint8_t) (tag >> 24); + buf[1] = (char) (uint8_t) (tag >> 16); + buf[2] = (char) (uint8_t) (tag >> 8); + buf[3] = (char) (uint8_t) (tag >> 0); +} + /* hb_direction_t */ diff --git a/src/hb-common.h b/src/hb-common.h index 920bd3278..cc221d33b 100644 --- a/src/hb-common.h +++ b/src/hb-common.h @@ -95,10 +95,14 @@ typedef uint32_t hb_tag_t; #define HB_TAG_NONE HB_TAG(0,0,0,0) -/* len=-1 means str is NUL-terminated */ +/* len=-1 means str is NUL-terminated. */ hb_tag_t hb_tag_from_string (const char *str, int len); +/* buf should have 4 bytes. */ +void +hb_tag_to_string (hb_tag_t tag, char *buf); + /* hb_direction_t */ diff --git a/src/hb-shape.cc b/src/hb-shape.cc index 5aa587bc3..6619c194e 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,6 +23,7 @@ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. * * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod */ #include "hb-private.hh" @@ -32,6 +34,172 @@ #include "hb-font-private.hh" +static void +parse_space (const char **pp, const char *end) +{ + char c; +#define ISSPACE(c) ((c)==' '||(c)=='\f'||(c)=='\n'||(c)=='\r'||(c)=='\t'||(c)=='\v') + while (*pp < end && (c = **pp, ISSPACE (c))) + (*pp)++; +#undef ISSPACE +} + +static hb_bool_t +parse_char (const char **pp, const char *end, char c) +{ + parse_space (pp, end); + + if (*pp == end || **pp != c) + return false; + + (*pp)++; + return true; +} + +static hb_bool_t +parse_uint (const char **pp, const char *end, unsigned int *pv) +{ + char buf[32]; + strncpy (buf, *pp, end - *pp); + buf[ARRAY_LENGTH (buf) - 1] = '\0'; + + char *p = buf; + char *pend = p; + unsigned int v; + + v = strtol (p, &pend, 0); + + if (p == pend) + return false; + + *pv = v; + *pp += pend - p; + return true; +} + +static hb_bool_t +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; +} + +static hb_bool_t +parse_feature_tag (const char **pp, const char *end, hb_feature_t *feature) +{ + const char *p = *pp; + char c; + + parse_space (pp, end); + +#define ISALNUM(c) (('a' <= (c) && (c) <= 'z') || ('A' <= (c) && (c) <= 'Z') || ('0' <= (c) && (c) <= '9')) + while (*pp < end && (c = **pp, ISALNUM(c))) + (*pp)++; +#undef ISALNUM + + if (p == *pp) + return false; + + feature->tag = hb_tag_from_string (p, *pp - p); + return true; +} + +static hb_bool_t +parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature) +{ + parse_space (pp, end); + + hb_bool_t 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 parse_char (pp, end, ']'); +} + +static hb_bool_t +parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature) +{ + return !parse_char (pp, end, '=') || parse_uint (pp, end, &feature->value); +} + + +static hb_bool_t +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) && + *pp == end; +} + +hb_bool_t +hb_feature_from_string (const char *str, int len, + hb_feature_t *feature) +{ + if (len < 0) + len = strlen (str); + + return parse_one_feature (&str, str + len, feature); +} + +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->start != (unsigned int) -1) + { + s[len++] = '['; + if (feature->start) + len += snprintf (s + len, ARRAY_LENGTH (s) - len, "%d", feature->start); + if (feature->end != feature->start + 1) { + s[len++] = ':'; + if (feature->end != (unsigned int) -1) + len += snprintf (s + len, ARRAY_LENGTH (s) - len, "%d", feature->end); + } + s[len++] = ']'; + } + if (feature->value > 1) + { + s[len++] = '='; + len += snprintf (s + len, ARRAY_LENGTH (s) - len, "%d", feature->value); + } + assert (len < ARRAY_LENGTH (s)); + len = MIN (len, size - 1); + memcpy (buf, s, len); + s[len] = '\0'; +} + + static const char **static_shaper_list; static diff --git a/src/hb-shape.h b/src/hb-shape.h index 84bf3e76e..90a188d2a 100644 --- a/src/hb-shape.h +++ b/src/hb-shape.h @@ -1,5 +1,6 @@ /* * Copyright © 2009 Red Hat, Inc. + * Copyright © 2012 Google, Inc. * * 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 + * Google Author(s): Behdad Esfahbod */ #ifndef HB_H_IN @@ -45,6 +47,17 @@ typedef struct hb_feature_t { unsigned int end; } hb_feature_t; +/* len=-1 means str is NUL-terminated */ +hb_bool_t +hb_feature_from_string (const char *str, int len, + hb_feature_t *feature); + +/* something like 128 bytes is more than enough. + * nul-terminates. */ +void +hb_feature_to_string (hb_feature_t *feature, + char *buf, unsigned int size); + void hb_shape (hb_font_t *font, diff --git a/util/options.cc b/util/options.cc index c05cee6d0..dc7aeed53 100644 --- a/util/options.cc +++ b/util/options.cc @@ -196,130 +196,6 @@ list_shapers (const char *name G_GNUC_UNUSED, } - -static void -parse_space (char **pp) -{ - char c; -#define ISSPACE(c) ((c)==' '||(c)=='\f'||(c)=='\n'||(c)=='\r'||(c)=='\t'||(c)=='\v') - while (c = **pp, ISSPACE (c)) - (*pp)++; -#undef ISSPACE -} - -static hb_bool_t -parse_char (char **pp, char c) -{ - parse_space (pp); - - if (**pp != c) - return false; - - (*pp)++; - return true; -} - -static hb_bool_t -parse_uint (char **pp, unsigned int *pv) -{ - char *p = *pp; - unsigned int v; - - v = strtol (p, pp, 0); - - if (p == *pp) - return false; - - *pv = v; - return true; -} - - -static hb_bool_t -parse_feature_value_prefix (char **pp, hb_feature_t *feature) -{ - if (parse_char (pp, '-')) - feature->value = 0; - else { - parse_char (pp, '+'); - feature->value = 1; - } - - return true; -} - -static hb_bool_t -parse_feature_tag (char **pp, hb_feature_t *feature) -{ - char *p = *pp, c; - - parse_space (pp); - -#define ISALNUM(c) (('a' <= (c) && (c) <= 'z') || ('A' <= (c) && (c) <= 'Z') || ('0' <= (c) && (c) <= '9')) - while (c = **pp, ISALNUM(c)) - (*pp)++; -#undef ISALNUM - - if (p == *pp) - return false; - - feature->tag = hb_tag_from_string (p, *pp - p); - return true; -} - -static hb_bool_t -parse_feature_indices (char **pp, hb_feature_t *feature) -{ - parse_space (pp); - - hb_bool_t has_start; - - feature->start = 0; - feature->end = (unsigned int) -1; - - if (!parse_char (pp, '[')) - return true; - - has_start = parse_uint (pp, &feature->start); - - if (parse_char (pp, ':')) { - parse_uint (pp, &feature->end); - } else { - if (has_start) - feature->end = feature->start + 1; - } - - return parse_char (pp, ']'); -} - -static hb_bool_t -parse_feature_value_postfix (char **pp, hb_feature_t *feature) -{ - return !parse_char (pp, '=') || parse_uint (pp, &feature->value); -} - - -static hb_bool_t -parse_one_feature (char **pp, hb_feature_t *feature) -{ - return parse_feature_value_prefix (pp, feature) && - parse_feature_tag (pp, feature) && - parse_feature_indices (pp, feature) && - parse_feature_value_postfix (pp, feature) && - (parse_char (pp, ',') || **pp == '\0'); -} - -static void -skip_one_feature (char **pp) -{ - char *e; - e = strchr (*pp, ','); - if (e) - *pp = e + 1; - else - *pp = *pp + strlen (*pp); -} - static gboolean parse_features (const char *name G_GNUC_UNUSED, const char *arg, @@ -351,11 +227,11 @@ parse_features (const char *name G_GNUC_UNUSED, /* now do the actual parsing */ p = s; shape_opts->num_features = 0; - while (*p) { - if (parse_one_feature (&p, &shape_opts->features[shape_opts->num_features])) + while (p && *p) { + char *end = strchr (p, ','); + if (hb_feature_from_string (p, end ? end - p : -1, &shape_opts->features[shape_opts->num_features])) shape_opts->num_features++; - else - skip_one_feature (&p); + p = end ? end + 1 : NULL; } return true;