+ ranges = malloc(sizeof(Range));
+ ranges[0].first = 0;
+ ranges[0].last = 255;
+ n_ranges = 1;
+ }
+ else
+ sort_and_compact_ranges(ranges, &n_ranges);
+
+ err = init_font(&font, face, ranges, n_ranges, autohinter, distfield, border);
+ if(err)
+ return 1;
+
+ if(!font.n_glyphs)
+ {
+ fprintf(stderr, "No glyphs found in the requested range\n");
+ return 1;
+ }
+
+ if(pack)
+ err = render_packed(&font, margin, padding);
+ else
+ err = render_grid(&font, cellw, cellh, cpl, seq);
+ if(err)
+ return 1;
+
+ err = save_png(out_fn, &font.image, (alpha && !distfield), (invert || distfield), npot);
+ if(err)
+ return 1;
+
+ if(def_fn)
+ save_defs(def_fn, &font);
+
+ for(i=0; (unsigned)i<font.n_glyphs; ++i)
+ free(font.glyphs[i].image.data);
+ free(font.glyphs);
+ free(font.kerning);
+ free(font.image.data);
+ free(ranges);
+
+ FT_Done_Face(face);
+ FT_Done_FreeType(freetype);
+
+ return 0;
+}
+
+void usage(void)
+{
+ printf("ttf2png 2.0 - True Type Font to PNG converter\n"
+ "Copyright (c) 2004-2021 Mikko Rasa, Mikkosoft Productions\n"
+ "Distributed under the GNU General Public License\n\n");
+
+ printf("Usage: ttf2png [options] <TTF file>\n\n");
+
+ printf("Accepted options (default values in [brackets])\n"
+ " -r Range of code points to convert [0,255]\n"
+ " -s Font size to use, in pixels [10]\n"
+ " -l Number of glyphs to put in one line [auto]\n"
+ " -c Glyph cell size, in pixels (grid mode only) [auto]\n"
+ " -o Output file name (or - for stdout) [font.png]\n");
+ printf(" -a Force autohinter\n"
+ " -t Render glyphs to alpha channel\n"
+ " -i Invert colors of the glyphs\n"
+ " -v Increase the level of verbosity\n"
+ " -e Use cells in sequence, without gaps (grid mode only)\n"
+ " -p Pack the glyphs tightly instead of in a grid\n"
+ " -m Margin around image edges (packed mode only) [0]\n"
+ " -n Padding between glyphs (packed mode only) [1]\n"
+ " -g Allow non-power-of-two result\n");
+ printf(" -f Create a distance field texture\n"
+ " -b Specify distance field border zone width\n"
+ " -d File name for writing glyph definitions\n"
+ " -h Print this message\n");
+}
+
+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>=(int)range->first && !*ptr)
+ {
+ range->last = value;
+ return;
+ }
+ }
+
+ printf("Invalid option argument in -%c %s\n", opt, optarg);
+ exit(1);
+}
+
+int 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 -1;
+ if(bytes<2)
+ return -1;
+
+ 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)
+{
+ unsigned i, j;
+ unsigned size = 0;
+ int scale = (distfield>0 ? distfield : 1);
+
+ font->ascent = (face->size->metrics.ascender/scale+63)/64;
+ font->descent = (face->size->metrics.descender/scale-63)/64;
+
+ if(verbose>=1)
+ {
+ printf("Ascent: %d\n", font->ascent);
+ printf("Descent: %d\n", font->descent);
+ }
+
+ font->n_glyphs = 0;
+ font->glyphs = NULL;
+ for(i=0; i<n_ranges; ++i)
+ if(init_glyphs(font, face, &ranges[i], autohinter, distfield, border))
+ return -1;
+
+ if(verbose>=1)
+ printf("Loaded %u glyphs\n", font->n_glyphs);
+
+ font->n_kerning = 0;
+ font->kerning = NULL;
+ for(i=0; i<font->n_glyphs; ++i) for(j=0; j<font->n_glyphs; ++j)
+ if(j!=i)
+ {
+ FT_Vector kerning;
+ FT_Get_Kerning(face, font->glyphs[i].index, font->glyphs[j].index, FT_KERNING_DEFAULT, &kerning);
+
+ /* FreeType documentation says that vertical kerning is practically
+ never used, so we ignore it. */
+ if(kerning.x)
+ {
+ Kerning *kern;
+
+ if(font->n_kerning>=size)
+ {
+ size += 16;
+ font->kerning = (Kerning *)realloc(font->kerning, size*sizeof(Kerning));
+ }
+
+ kern = &font->kerning[font->n_kerning++];
+ kern->left_glyph = &font->glyphs[i];
+ kern->right_glyph = &font->glyphs[j];
+ kern->distance = (kerning.x/scale+32)/64;
+ }
+ }
+
+ if(verbose>=1)
+ printf("Loaded %d kerning pairs\n", font->n_kerning);
+
+ return 0;
+}
+
+int init_glyphs(Font *font, FT_Face face, const Range *range, bool autohinter, unsigned distfield, unsigned border)
+{
+ unsigned i, j;
+ unsigned size = font->n_glyphs;
+ int scale = (distfield>0 ? distfield : 1);
+
+ for(i=range->first; i<=range->last; ++i)
+ {
+ unsigned n;
+ FT_Bitmap *bmp = &face->glyph->bitmap;
+ int flags = 0;
+ Glyph *glyph;
+
+ n = FT_Get_Char_Index(face, i);
+ if(!n)
+ continue;
+
+ if(autohinter)
+ flags |= FT_LOAD_FORCE_AUTOHINT;
+ FT_Load_Glyph(face, n, flags);
+ FT_Render_Glyph(face->glyph, (distfield ? FT_RENDER_MODE_MONO : FT_RENDER_MODE_NORMAL));
+