aboutsummaryrefslogtreecommitdiffstats
path: root/libfonts_decode_font_description.c
diff options
context:
space:
mode:
authorMattias Andrée <maandree@kth.se>2021-11-20 21:55:57 +0100
committerMattias Andrée <maandree@kth.se>2021-11-20 21:55:57 +0100
commit648533b47d5d8eb42f0922f584f482707714b17f (patch)
tree10cebfbd5cdcd90f7c23caac1d5e9ca6b3022f25 /libfonts_decode_font_description.c
parentAdd prototype for libfonts_do_decoded_font_descriptions_match (diff)
downloadlibfonts-648533b47d5d8eb42f0922f584f482707714b17f.tar.gz
libfonts-648533b47d5d8eb42f0922f584f482707714b17f.tar.bz2
libfonts-648533b47d5d8eb42f0922f584f482707714b17f.tar.xz
Implement libfonts_decode_font_description
Signed-off-by: Mattias Andrée <maandree@kth.se>
Diffstat (limited to 'libfonts_decode_font_description.c')
-rw-r--r--libfonts_decode_font_description.c216
1 files changed, 216 insertions, 0 deletions
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;
+}