diff options
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | libfonts_decode_font_description.c | 216 |
2 files changed, 217 insertions, 0 deletions
@@ -18,6 +18,7 @@ LIB_NAME = fonts OBJ =\ libfonts_calculate_subpixel_order.o\ + libfonts_decode_font_description.o\ libfonts_encode_font_description.o\ libfonts_get_output_dpi.o diff --git a/libfonts_decode_font_description.c b/libfonts_decode_font_description.c new file mode 100644 index 0000000..329d338 --- /dev/null +++ b/libfonts_decode_font_description.c @@ -0,0 +1,216 @@ +/* See LICENSE file for copyright and license details. */ +#include "libfonts.h" +#include <ctype.h> +#include <errno.h> +#include <inttypes.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define LIST_FIELDS_EXCEPT_FINAL(X)\ + X(foundry)\ + X(family_name)\ + X(weight_name)\ + X(slant)\ + X(setwidth_name)\ + X(add_style_name)\ + X(pixel_size)\ + X(point_size)\ + X(resolution_x)\ + X(resolution_y)\ + X(spacing)\ + X(average_width)\ + X(charset_registry) + +struct range { + uint32_t first; + uint32_t last; +}; + +static int +parse_hexadecimal(const char *s, const char **endp, uint32_t *valuep) +{ + uint64_t r = 0; + if (!isxdigit(*s)) + return -1; + for (;; s++) { + if (isdigit(*s)) + r = (r * 16) | (*s & 15); + else if (isxdigit(*s)) + r = (r * 16) | ((*s & 15) + 9); + else + break; + if (r > INT32_MAX) + return -1; + } + *endp = s; + *valuep = (uint32_t)r; + return 0; +} + +static int +parse_decimal(const char *s, const char **endp, uint32_t *valuep) +{ + uint64_t r = 0; + if (!isdigit(*s)) + return -1; + for (; isdigit(*s); s++) { + r = r * 10 + (*s & 15); + if (r > INT32_MAX) + return -1; + } + *endp = s; + *valuep = (uint32_t)r; + return 0; +} + +static int +parse_number(const char *s, const char **endp, uint32_t *valuep) +{ + if (s[0] == '0' && s[1] == 'x') + return parse_hexadecimal(&s[2], endp, valuep); + else + return parse_decimal(&s[0], endp, valuep); +} + +static int +cmprange(const void *av, const void *bv) +{ + const struct range *a = av, *b = bv; + return a->first < b->first ? -1 : a->first > b->first; +} + +static int +fix_charset_subset(char *out, const char *in) +{ + struct range ranges[128], saved; + size_t i, n = 0; + const char *s; + uint32_t t; + + for (s = in; *s; s++) { + if (*s == ' ') + continue; + if (parse_number(s, &s, &ranges[0].first)) + return -1; + if (*s == '_') { + if (parse_number(&s[1], &s, &ranges[0].last)) + return -1; + if (ranges[0].first > ranges[0].last) { + t = ranges[0].first; + ranges[0].first = ranges[0].last; + ranges[0].last = t; + } + } else { + ranges[0].last = ranges[0].first; + } + if (*s && *s != ' ') + return -1; + } + + if (!n) { + *out = '\0'; + return 0; + } + + qsort(ranges, n, sizeof(*ranges), cmprange); + saved = ranges[0]; + for (i = 1; i < n; i++) { + if (ranges[i].first <= saved.last) { + saved.last = ranges[i].last; + } else { + if (saved.first == saved.last) + out += sprintf(out, "%"PRId32" ", saved.first); + else + out += sprintf(out, "%"PRId32"-%"PRId32" ", saved.first, saved.last); + saved = ranges[i]; + } + } + if (saved.first == saved.last) + out += sprintf(out, "%"PRId32" ", saved.first); + else + out += sprintf(out, "%"PRId32"-%"PRId32" ", saved.first, saved.last); + + out[-1] = '\0'; + return 0; +} + +int +libfonts_decode_font_description(struct libfonts_font_description *desc, const char *in) +{ + char subset[256]; + const char *s = in; + char *buf, *ss = subset; + int have_subset = 0; + int in_subset = 0; + + if (strlen(in) > 255) { + errno = EINVAL; + return -1; + } + + memset(desc, 0, sizeof(*desc)); + buf = desc->_buf; + + if (*s == '+') { + desc->xlfd_version = buf; + for (s++; *s && *s != '-'; s++) + *buf++ = *s == '~' ? '-' : *s; + *buf++ = '\0'; + } + +#define X(F)\ + if (*s != '-')\ + goto private;\ + desc->F = buf;\ + for (s++; *s && *s != '-'; s++)\ + *buf++ = *s == '~' ? '-' : *s;\ + *buf++ = '\0'; + LIST_FIELDS_EXCEPT_FINAL(X) +#undef X + + if (*s != '-') + goto private; + desc->charset_encoding = buf; + for (s++; *s && *s != '-'; s++) { + if (in_subset) { + if (*s == ']') + in_subset = 0; + else + *ss++ = *s == '~' ? '-' : *s; + } else { + if (*s == '[') { + in_subset = 1; + if (have_subset) + *ss++ = ' '; + else + have_subset = 1; + } else { + *buf++ = *s == '~' ? '-' : *s; + } + } + } + *buf++ = '\0'; + if (in_subset) + goto private; + if (have_subset) { + *ss = '\0'; + desc->charset_subset = buf; + if (fix_charset_subset(buf, subset)) + goto private; + } + + if (*s == '-') { + desc->unrecognised_fields = buf; + stpcpy(buf, &s[1]); + } + + return 0; + +private: + memset(desc, 0, sizeof(*desc)); + stpcpy(desc->_buf, in); + desc->private_font_name = desc->_buf; + return 0; +} |