+int convert_numeric_option(char opt, int min_value)
+{
+ int value;
+ char *ptr;
+
+ value = strtol(optarg, &ptr, 0);
+ if(value<min_value || *ptr)
+ {
+ printf("Invalid option argument in -%c %s\n", opt, optarg);
+ exit(1);
+ }
+
+ return value;
+}
+
+void convert_code_point_range(char opt, Range *range)
+{
+ int value;
+ char *ptr;
+
+ if(!strcmp(optarg, "all"))
+ {
+ range->first = 0;
+ range->last = 0x10FFFF;
+ return;
+ }
+
+ value = str_to_code_point(optarg, &ptr);
+ if(value>0 && *ptr==',')
+ {
+ range->first = value;
+ value = str_to_code_point(ptr+1, &ptr);
+ if(value>0 && !*ptr)
+ {
+ range->last = value;
+ return;
+ }
+ }
+
+ printf("Invalid option argument in -%c %s\n", opt, optarg);
+ exit(1);
+}
+
+unsigned str_to_code_point(const char *nptr, char **endptr)
+{
+ if(nptr[0]=='U' && nptr[1]=='+')
+ return strtoul(nptr+2, endptr, 16);
+ else if(nptr[0]&0x80)
+ {
+ unsigned bytes;
+ unsigned code;
+ unsigned i;
+
+ if(endptr)
+ *endptr = (char *)nptr;
+
+ for(bytes=1; (bytes<4 && (nptr[0]&(0x80>>bytes))); ++bytes)
+ if((nptr[bytes]&0xC0)!=0x80)
+ return 0;
+ if(bytes<2)
+ return 0;
+
+ code = nptr[0]&(0x3F>>bytes);
+ for(i=1; i<bytes; ++i)
+ code = (code<<6)|(nptr[i]&0x3F);
+
+ if(endptr)
+ *endptr = (char *)nptr+bytes;
+
+ return code;
+ }
+ else if(isdigit(nptr[0]))
+ return strtoul(nptr, endptr, 0);
+ else
+ {
+ if(endptr)
+ *endptr = (char *)nptr+1;
+ return *nptr;
+ }
+}
+
+void convert_size(char opt, unsigned *width, unsigned *height)
+{
+ int value;
+ char *ptr;
+
+ if(!strcmp(optarg, "auto"))
+ {
+ *width = 0;
+ *height = 0;
+ return;
+ }
+ else if(!strcmp(optarg, "autorect"))
+ {
+ *width = 0;
+ *height = 1;
+ return;
+ }
+
+ value = strtol(optarg, &ptr, 0);
+ if(value>0)
+ {
+ *width = value;
+ if(*ptr=='x')
+ {
+ value = strtol(ptr+1, &ptr, 0);
+ if(value>0 && !*ptr)
+ {
+ *height = value;
+ return;
+ }
+ }
+ else if(!*ptr)
+ {
+ *height = *width;
+ return;
+ }
+ }
+
+ printf("Invalid option argument in -%c %s\n", opt, optarg);
+ exit(1);
+}
+
+void sort_and_compact_ranges(Range *ranges, unsigned *n_ranges)
+{
+ unsigned i, j;
+
+ if(!*n_ranges)
+ return;
+
+ qsort(ranges, *n_ranges, sizeof(Range), &range_cmp);
+ for(i=0, j=1; j<*n_ranges; ++j)
+ {
+ if(ranges[i].last+1>=ranges[j].first)
+ {
+ if(ranges[j].last>ranges[i].last)
+ ranges[i].last = ranges[j].last;
+ }
+ else
+ {
+ ++i;
+ if(i!=j)
+ ranges[i] = ranges[j];
+ }
+ }
+
+ *n_ranges = i+1;
+}
+
+int range_cmp(const void *p1, const void *p2)
+{
+ const Range *r1 = (const Range *)p1;
+ const Range *r2 = (const Range *)p2;
+ if(r1->first!=r2->first)
+ return (r1->first<r2->first ? -1 : 1);
+ else if(r1->last!=r2->last)
+ return (r1->last<r2->last ? -1 : 1);
+ else
+ return 0;
+}
+
+unsigned round_to_pot(unsigned n)
+{
+ n -= 1;
+ n |= n>>1;
+ n |= n>>2;
+ n |= n>>4;
+ n |= n>>8;
+ n |= n>>16;
+
+ return n+1;
+}
+
+int init_image(Image *image, size_t w, size_t h)
+{
+ size_t s;
+
+ image->w = w;
+ image->h = h;
+ image->data = NULL;
+ image->border = 0;
+
+ if(!image->w || !image->h)
+ return 0;
+
+ s = w*h;
+ if(s/h!=w)
+ {
+ fprintf(stderr, "Cannot allocate memory for a %dx%d image\n", image->w, image->h);
+ return -1;
+ }
+
+ image->data = malloc(s);
+ if(!image->data)
+ {
+ fprintf(stderr, "Cannot allocate memory for a %dx%d image\n", image->w, image->h);
+ return -1;
+ }
+
+ return 0;
+}
+
+int init_font(Font *font, FT_Face face, const Range *ranges, unsigned n_ranges, bool autohinter, unsigned distfield, unsigned border)