diff --git a/src/hb-common.cc b/src/hb-common.cc index 3564e4355..04d9fb09a 100644 --- a/src/hb-common.cc +++ b/src/hb-common.cc @@ -605,3 +605,253 @@ hb_version_atleast (unsigned int major, { return HB_VERSION_ATLEAST (major, minor, micro); } + + +/* hb_feature_t */ + +static bool +parse_space (const char **pp, const char *end) +{ + while (*pp < end && ISSPACE (**pp)) + (*pp)++; + return true; +} + +static bool +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 bool +parse_uint (const char **pp, const char *end, unsigned int *pv) +{ + 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 bool +parse_bool (const char **pp, const char *end, unsigned int *pv) +{ + 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; +} + +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 == '"')) + { + quote = **pp; + (*pp)++; + } + + const char *p = *pp; + while (*pp < end && ISALNUM(**pp)) + (*pp)++; + + if (p == *pp || *pp - p > 4) + return false; + + 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 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): a string to parse + * @len: length of @str, or -1 if string is %NULL terminated + * @feature: (out): the #hb_feature_t to initialize with the parsed values + * + * Parses a string into a #hb_feature_t. + * + * TODO: document the syntax here. + * + * Return value: + * %true if @str is successfully parsed, %false otherwise. + * + * Since: 0.9.5 + **/ +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: an #hb_feature_t to convert + * @buf: (array length=size) (out): output string + * @size: the allocated size of @buf + * + * Converts a #hb_feature_t into a %NULL-terminated string in the format + * understood by hb_feature_from_string(). The client in responsible for + * allocating big enough size for @buf, 128 bytes is more than enough. + * + * Since: 0.9.5 + **/ +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'; +} diff --git a/src/hb-common.h b/src/hb-common.h index 2cbee76a8..dd72a9dd2 100644 --- a/src/hb-common.h +++ b/src/hb-common.h @@ -362,6 +362,24 @@ typedef struct hb_user_data_key_t { typedef void (*hb_destroy_func_t) (void *user_data); +/* Font features. */ + +typedef struct hb_feature_t { + hb_tag_t tag; + uint32_t value; + unsigned int start; + unsigned int end; +} hb_feature_t; + +HB_EXTERN hb_bool_t +hb_feature_from_string (const char *str, int len, + hb_feature_t *feature); + +HB_EXTERN void +hb_feature_to_string (hb_feature_t *feature, + char *buf, unsigned int size); + + HB_END_DECLS #endif /* HB_COMMON_H */ diff --git a/src/hb-shape.cc b/src/hb-shape.cc index 706f14420..f57cad903 100644 --- a/src/hb-shape.cc +++ b/src/hb-shape.cc @@ -45,254 +45,6 @@ * contains the output glyphs and their positions. **/ -static bool -parse_space (const char **pp, const char *end) -{ - while (*pp < end && ISSPACE (**pp)) - (*pp)++; - return true; -} - -static bool -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 bool -parse_uint (const char **pp, const char *end, unsigned int *pv) -{ - 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 bool -parse_bool (const char **pp, const char *end, unsigned int *pv) -{ - 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; -} - -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 == '"')) - { - quote = **pp; - (*pp)++; - } - - const char *p = *pp; - while (*pp < end && ISALNUM(**pp)) - (*pp)++; - - if (p == *pp || *pp - p > 4) - return false; - - 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 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): a string to parse - * @len: length of @str, or -1 if string is %NULL terminated - * @feature: (out): the #hb_feature_t to initialize with the parsed values - * - * Parses a string into a #hb_feature_t. - * - * TODO: document the syntax here. - * - * Return value: - * %true if @str is successfully parsed, %false otherwise. - * - * Since: 0.9.5 - **/ -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: an #hb_feature_t to convert - * @buf: (array length=size) (out): output string - * @size: the allocated size of @buf - * - * Converts a #hb_feature_t into a %NULL-terminated string in the format - * understood by hb_feature_from_string(). The client in responsible for - * allocating big enough size for @buf, 128 bytes is more than enough. - * - * Since: 0.9.5 - **/ -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 diff --git a/src/hb-shape.h b/src/hb-shape.h index 53bb845bf..39507ff74 100644 --- a/src/hb-shape.h +++ b/src/hb-shape.h @@ -40,22 +40,6 @@ HB_BEGIN_DECLS -typedef struct hb_feature_t { - hb_tag_t tag; - uint32_t value; - unsigned int start; - unsigned int end; -} hb_feature_t; - -HB_EXTERN hb_bool_t -hb_feature_from_string (const char *str, int len, - hb_feature_t *feature); - -HB_EXTERN void -hb_feature_to_string (hb_feature_t *feature, - char *buf, unsigned int size); - - HB_EXTERN void hb_shape (hb_font_t *font, hb_buffer_t *buffer,