2 ttf2png - True Type Font to PNG converter
3 Copyright (c) 2004-2008 Mikko Rasa
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 #include FT_FREETYPE_H
45 typedef struct sKerning
73 int convert_numeric_option(char, int);
74 void convert_code_point_range(char, Range *);
75 unsigned str_to_code_point(const char *, char **);
76 void convert_size(char, unsigned *, unsigned *);
77 void sort_and_compact_ranges(Range *, unsigned *);
78 int range_cmp(const void *, const void *);
79 unsigned round_to_pot(unsigned);
80 void *alloc_image_data(size_t, size_t);
81 int init_font(Font *, FT_Face, const Range *, unsigned, bool);
82 int init_glyphs(Font *, FT_Face, const Range *, bool);
83 int render_grid(Font *, unsigned, unsigned, unsigned, bool);
84 int render_packed(Font *, unsigned, unsigned);
85 int save_defs(const char *, const Font *);
86 int save_png(const char *, const Image *, char);
90 int main(int argc, char **argv)
94 unsigned n_ranges = 0;
105 unsigned padding = 1;
113 char *out_fn = "font.png";
124 while((i = getopt(argc, argv, "r:s:l:c:o:atvh?ed:pim:n:")) != -1)
129 ranges = (Range *)realloc(ranges, (++n_ranges)*sizeof(Range));
130 convert_code_point_range('r', &ranges[n_ranges-1]);
133 size = convert_numeric_option('s', 1);
136 cpl = convert_numeric_option('l', 1);
139 convert_size('c', &cellw, &cellh);
170 margin = convert_numeric_option('m', 0);
173 padding = convert_numeric_option('n', 0);
177 if(!strcmp(out_fn, "-"))
188 err = FT_Init_FreeType(&freetype);
191 fprintf(stderr, "Couldn't initialize FreeType library\n");
195 err = FT_New_Face(freetype, fn, 0, &face);
198 fprintf(stderr, "Couldn't load font file\n");
199 if(err==FT_Err_Unknown_File_Format)
200 fprintf(stderr, "Unknown file format\n");
206 const char *name = FT_Get_Postscript_Name(face);
207 printf("Font name: %s\n", name);
208 printf("Glyphs: %ld\n", face->num_glyphs);
211 err = FT_Set_Pixel_Sizes(face, 0, size);
214 fprintf(stderr, "Couldn't set size\n");
219 sort_and_compact_ranges(ranges, &n_ranges);
220 err = init_font(&font, face, ranges, n_ranges, autohinter);
226 fprintf(stderr, "No glyphs found in the requested range\n");
231 err = render_packed(&font, margin, padding);
233 err = render_grid(&font, cellw, cellh, cpl, seq);
239 for(i=0; (unsigned)i<font.image.w*font.image.h; ++i)
240 font.image.data[i] = 255-font.image.data[i];
242 err = save_png(out_fn, &font.image, alpha);
247 save_defs(def_fn, &font);
249 for(i=0; (unsigned)i<font.n_glyphs; ++i)
250 free(font.glyphs[i].image.data);
253 free(font.image.data);
256 FT_Done_FreeType(freetype);
263 printf("ttf2png 1.1 - True Type Font to PNG converter\n"
264 "Copyright (c) 2004-2018 Mikko Rasa, Mikkosoft Productions\n"
265 "Distributed under the GNU General Public License\n\n");
267 printf("Usage: ttf2png [options] <TTF file>\n\n");
269 printf("Accepted options (default values in [brackets])\n"
270 " -r Range of code points to convert [0,255]\n"
271 " -s Font size to use, in pixels [10]\n"
272 " -l Number of glyphs to put in one line [auto]\n"
273 " -c Glyph cell size, in pixels (grid mode only) [auto]\n"
274 " -o Output file name (or - for stdout) [font.png]\n");
275 printf(" -a Force autohinter\n"
276 " -t Render glyphs to alpha channel\n"
277 " -i Invert colors of the glyphs\n"
278 " -v Increase the level of verbosity\n"
279 " -e Use cells in sequence, without gaps (grid mode only)\n"
280 " -p Pack the glyphs tightly instead of in a grid\n"
281 " -m Margin around image edges (packed mode only) [0]\n"
282 " -n Padding between glyphs (packed mode only) [1]\n"
283 " -d File name for writing glyph definitions\n"
284 " -h Print this message\n");
287 int convert_numeric_option(char opt, int min_value)
292 value = strtol(optarg, &ptr, 0);
293 if(value<min_value || *ptr)
295 printf("Invalid option argument in -%c %s\n", opt, optarg);
302 void convert_code_point_range(char opt, Range *range)
307 if(!strcmp(optarg, "all"))
310 range->last = 0x10FFFF;
314 value = str_to_code_point(optarg, &ptr);
315 if(value>0 && *ptr==',')
317 range->first = value;
318 value = str_to_code_point(ptr+1, &ptr);
326 printf("Invalid option argument in -%c %s\n", opt, optarg);
330 unsigned str_to_code_point(const char *nptr, char **endptr)
332 if(nptr[0]=='U' && nptr[1]=='+')
333 return strtoul(nptr+2, endptr, 16);
334 else if(nptr[0]&0x80)
341 *endptr = (char *)nptr;
343 for(bytes=1; (bytes<4 && (nptr[0]&(0x80>>bytes))); ++bytes)
344 if((nptr[bytes]&0xC0)!=0x80)
349 code = nptr[0]&(0x3F>>bytes);
350 for(i=1; i<bytes; ++i)
351 code = (code<<6)|(nptr[i]&0x3F);
354 *endptr = (char *)nptr+bytes;
358 else if(isdigit(nptr[0]))
359 return strtoul(nptr, endptr, 0);
363 *endptr = (char *)nptr+1;
368 void convert_size(char opt, unsigned *width, unsigned *height)
373 if(!strcmp(optarg, "auto"))
379 else if(!strcmp(optarg, "autorect"))
386 value = strtol(optarg, &ptr, 0);
392 value = strtol(ptr+1, &ptr, 0);
406 printf("Invalid option argument in -%c %s\n", opt, optarg);
410 void sort_and_compact_ranges(Range *ranges, unsigned *n_ranges)
417 qsort(ranges, *n_ranges, sizeof(Range), &range_cmp);
418 for(i=0, j=1; j<*n_ranges; ++j)
420 if(ranges[i].last+1>=ranges[j].first)
422 if(ranges[j].last>ranges[i].last)
423 ranges[i].last = ranges[j].last;
429 ranges[i] = ranges[j];
436 int range_cmp(const void *p1, const void *p2)
438 const Range *r1 = (const Range *)p1;
439 const Range *r2 = (const Range *)p2;
440 if(r1->first!=r2->first)
441 return (r1->first<r2->first ? -1 : 1);
442 else if(r1->last!=r2->last)
443 return (r1->last<r2->last ? -1 : 1);
448 unsigned round_to_pot(unsigned n)
460 void *alloc_image_data(size_t a, size_t b)
464 /* Carry out the multiplication manually so we can check for overflow. */
473 fprintf(stderr, "Cannot allocate %lu kbytes of memory for image\n", (unsigned long)(c/1024*b));
480 fprintf(stderr, "Cannot allocate %lu kbytes of memory for image\n", (unsigned long)(a/1024*b));
484 int init_font(Font *font, FT_Face face, const Range *ranges, unsigned n_ranges, bool autohinter)
489 font->ascent = (face->size->metrics.ascender+63)>>6;
490 font->descent = (face->size->metrics.descender+63)>>6;
494 printf("Ascent: %d\n", font->ascent);
495 printf("Descent: %d\n", font->descent);
500 for(i=0; i<n_ranges; ++i)
501 if(init_glyphs(font, face, &ranges[i], autohinter))
505 printf("Loaded %u glyphs\n", font->n_glyphs);
508 font->kerning = NULL;
509 for(i=0; i<font->n_glyphs; ++i) for(j=0; j<font->n_glyphs; ++j)
513 FT_Get_Kerning(face, font->glyphs[i].index, font->glyphs[j].index, FT_KERNING_DEFAULT, &kerning);
515 /* FreeType documentation says that vertical kerning is practically
516 never used, so we ignore it. */
521 if(font->n_kerning>=size)
524 font->kerning = (Kerning *)realloc(font->kerning, size*sizeof(Kerning));
527 kern = &font->kerning[font->n_kerning++];
528 kern->left_code = font->glyphs[i].code;
529 kern->right_code = font->glyphs[j].code;
530 kern->distance = kerning.x/64;
535 printf("Loaded %d kerning pairs\n", font->n_kerning);
540 int init_glyphs(Font *font, FT_Face face, const Range *range, bool autohinter)
543 unsigned size = font->n_glyphs;
545 for(i=range->first; i<=range->last; ++i)
548 FT_Bitmap *bmp = &face->glyph->bitmap;
553 n = FT_Get_Char_Index(face, i);
558 flags |= FT_LOAD_FORCE_AUTOHINT;
559 FT_Load_Glyph(face, n, flags);
560 FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
564 printf(" Code point U+%04X", i);
565 if(i>=0x20 && i<0x7F)
567 else if(i>=0xA0 && i<=0x10FFFF)
572 for(bytes=2; i>>(1+bytes*5); ++bytes) ;
573 for(j=0; j<bytes; ++j)
574 utf8[j] = 0x80 | ((i>>((bytes-j-1)*6))&0x3F);
575 utf8[0] |= 0xF0<<(4-bytes);
578 printf(" (%s)", utf8);
580 printf(": glyph %u, size %dx%d\n", n, bmp->width, bmp->rows);
583 if(bmp->pixel_mode!=FT_PIXEL_MODE_GRAY)
585 fprintf(stderr, "Warning: Glyph %u skipped, not grayscale\n", n);
589 if(font->n_glyphs>=size)
592 font->glyphs = (Glyph *)realloc(font->glyphs, size*sizeof(Glyph));
595 glyph = &font->glyphs[font->n_glyphs++];
598 glyph->image.w = bmp->width;
599 glyph->image.h = bmp->rows;
600 glyph->image.data = (char *)malloc(bmp->width*bmp->rows);
601 if(!glyph->image.data)
603 fprintf(stderr, "Cannot allocate %d bytes of memory for glyph\n", bmp->width*bmp->rows);
606 glyph->offset_x = face->glyph->bitmap_left;
607 glyph->offset_y = face->glyph->bitmap_top-bmp->rows;
608 glyph->advance = (int)(face->glyph->advance.x+32)/64;
610 /* Copy the glyph image since FreeType uses a global buffer, which would
611 be overwritten by the next glyph. Negative pitch means the scanlines
612 start from the bottom. */
615 for(y=0; y<bmp->rows; ++y) for(x=0; x<bmp->width; ++x)
616 glyph->image.data[x+(glyph->image.h-1-y)*glyph->image.w] = bmp->buffer[x-y*bmp->pitch];
620 for(y=0; y<bmp->rows; ++y) for(x=0; x<bmp->width; ++x)
621 glyph->image.data[x+y*glyph->image.w] = bmp->buffer[x+y*bmp->pitch];
628 int render_grid(Font *font, unsigned cellw, unsigned cellh, unsigned cpl, bool seq)
631 int top = 0, bot = 0;
632 unsigned first, last;
633 unsigned maxw = 0, maxh = 0;
635 /* Find extremes of the glyph images. */
636 for(i=0; i<font->n_glyphs; ++i)
640 y = font->glyphs[i].offset_y+font->glyphs[i].image.h;
643 if(font->glyphs[i].offset_y<bot)
644 bot = font->glyphs[i].offset_y;
645 if(font->glyphs[i].image.w>maxw)
646 maxw = font->glyphs[i].image.w;
647 if(font->glyphs[i].image.h>maxh)
648 maxh = font->glyphs[i].image.h;
653 /* Establish a large enough cell to hold all glyphs in the range. */
654 int square = (cellh==cellw);
668 printf("Max size: %u x %u\n", maxw, maxh);
669 printf("Y range: [%d %d]\n", bot, top);
670 printf("Cell size: %u x %u\n", cellw, cellh);
671 if(maxw>cellw || (unsigned)(top-bot)>cellh)
672 fprintf(stderr, "Warning: character size exceeds cell size\n");
677 /* Determine number of characters per line, trying to fit all the glyphs
678 in a square image. */
682 if(cpl>0 && font->n_glyphs/cpl*cellh<=cpl*cellw)
687 first = font->glyphs[0].code;
690 last = font->glyphs[font->n_glyphs-1].code;
692 font->image.w = round_to_pot(cpl*cellw);
694 font->image.h = round_to_pot((font->n_glyphs+cpl-1)/cpl*cellh);
696 font->image.h = round_to_pot((last-first+cpl)/cpl*cellh);
698 font->image.data = (char *)alloc_image_data(font->image.w, font->image.h);
699 if(!font->image.data)
701 memset(font->image.data, 255, font->image.w*font->image.h);
703 for(i=0; i<font->n_glyphs; ++i)
709 glyph = &font->glyphs[i];
714 ci = glyph->code-first;
719 if(cellw>glyph->image.w)
720 cx += (cellw-glyph->image.w)/2;
721 cy += top-glyph->offset_y-glyph->image.h;
726 for(y=0; y<glyph->image.h; ++y) for(x=0; x<glyph->image.w; ++x)
728 if(cx+x>=font->image.w || cy+y>=font->image.h)
730 font->image.data[cx+x+(cy+y)*font->image.w] = 255-glyph->image.data[x+y*glyph->image.w];
737 int render_packed(Font *font, unsigned margin, unsigned padding)
742 unsigned *used_pixels;
743 unsigned cx = margin, cy;
746 /* Compute the total area occupied by glyphs and padding. */
747 for(i=0; i<font->n_glyphs; ++i)
749 size_t a = area+(font->glyphs[i].image.w+padding)*(font->glyphs[i].image.h+padding);
752 fprintf(stderr, "Overflow in counting total glyph area\n");
758 /* Find an image size that's no higher than wide, allowing for some
759 imperfections in the packing. */
760 for(font->image.w=1;; font->image.w<<=1)
762 if(font->image.w<=margin*2)
764 font->image.h = (area*5/4)/(font->image.w-margin*2)+margin*2;
765 if(font->image.h<=font->image.w)
768 font->image.h = round_to_pot(font->image.h);
770 /* Allocate arrays for storing the image and keeping track of used pixels and
771 glyphs. Since glyphs are rectangular and the image is filled starting from
772 the top, it's enough to track the number of used pixels at the top of each
774 font->image.data = (char *)alloc_image_data(font->image.w, font->image.h);
775 if(!font->image.data)
777 memset(font->image.data, 255, font->image.w*font->image.h);
778 used_pixels = (unsigned *)malloc(font->image.w*sizeof(unsigned));
779 memset(used_pixels, 0, font->image.w*sizeof(unsigned));
780 used_glyphs = (char *)malloc(font->n_glyphs);
781 memset(used_glyphs, 0, font->n_glyphs);
783 for(cy=margin; cy+margin<font->image.h;)
788 unsigned best_score = 0;
789 unsigned target_h = 0;
791 /* Find the leftmost free pixel on this row. Also record the lowest
792 extent of glyphs to the left of the free position. */
793 for(; (cx+margin<font->image.w && used_pixels[cx]>cy); ++cx)
794 if(used_pixels[cx]-cy-padding>target_h)
795 target_h = used_pixels[cx]-cy-padding;
797 if(cx+margin>=font->image.w)
804 /* Count the free pixel at this position. */
805 for(w=0; (cx+w+margin<font->image.w && used_pixels[cx+w]<=cy); ++w) ;
807 /* Find a suitable glyph to put here. */
808 for(i=0; i<font->n_glyphs; ++i)
812 g = &font->glyphs[i];
813 if(!used_glyphs[i] && g->image.w<=w)
817 /* Prefer glyphs that would reach exactly as low as the ones left
818 of here. This aims to create a straight edge at the bottom for
819 lining up further glyphs. */
820 score = g->image.h+padding;
821 if(g->image.h==target_h)
840 used_glyphs[glyph-font->glyphs] = 1;
844 for(y=0; y<glyph->image.h; ++y) for(x=0; x<glyph->image.w; ++x)
846 if(cx+x>=font->image.w || cy+y>=font->image.h)
848 font->image.data[cx+x+(cy+y)*font->image.w] = 255-glyph->image.data[x+y*glyph->image.w];
850 for(x=0; x<glyph->image.w+2*padding; ++x)
852 if(cx+x<padding || cx+x>=font->image.w+padding)
854 if(used_pixels[cx+x-padding]<cy+glyph->image.h+padding)
855 used_pixels[cx+x-padding] = cy+glyph->image.h+padding;
858 if(cy+glyph->image.h+margin>used_h)
859 used_h = cy+glyph->image.h+margin;
862 /* Trim the image to the actually used size, in case the original estimate
863 was too pessimistic. */
864 font->image.h = round_to_pot(used_h);
872 int save_defs(const char *fn, const Font *font)
877 out = fopen(fn, "w");
880 fprintf(stderr, "Couldn't open %s\n",fn);
884 fprintf(out, "# Image/font info:\n");
885 fprintf(out, "# width height size ascent descent\n");
886 fprintf(out, "font %d %d %d %d %d\n", font->image.w, font->image.h, font->size, font->ascent, font->descent);
887 fprintf(out, "\n# Glyph info:\n");
888 fprintf(out, "# code x y width height offset_x offset_y advance\n");
889 for(i=0; i<font->n_glyphs; ++i)
891 const Glyph *g = &font->glyphs[i];
892 fprintf(out, "glyph %u %u %u %u %u %d %d %d\n", g->code, g->x, g->y, g->image.w, g->image.h, g->offset_x, g->offset_y, g->advance);
894 fprintf(out, "\n# Kerning info:\n");
895 fprintf(out, "# left right distance\n");
896 for(i=0; i<font->n_kerning; ++i)
898 const Kerning *k = &font->kerning[i];
899 fprintf(out, "kern %u %u %d\n", k->left_code, k->right_code, k->distance);
907 int save_png(const char *fn, const Image *image, char alpha)
921 out = fopen(fn, "wb");
924 fprintf(stderr, "Couldn't open %s\n",fn);
929 pngs = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
932 fprintf(stderr, "Error writing PNG file\n");
935 pngi = png_create_info_struct(pngs);
938 png_destroy_write_struct(&pngs, NULL);
939 fprintf(stderr, "Error writing PNG file\n");
943 png_init_io(pngs, out);
944 rows = (png_byte **)malloc(image->h*sizeof(png_byte *));
947 data2 = (png_byte *)alloc_image_data(image->w*2, image->h);
950 for(i=0; i<image->w*image->h; ++i)
953 data2[i*2+1] = 255-image->data[i];
955 for(i=0; i<image->h; ++i)
956 rows[i] = (png_byte *)(data2+i*image->w*2);
957 color = PNG_COLOR_TYPE_GRAY_ALPHA;
961 for(i=0; i<image->h; ++i)
962 rows[i] = (png_byte *)(image->data+i*image->w);
963 color = PNG_COLOR_TYPE_GRAY;
965 png_set_IHDR(pngs, pngi, image->w, image->h, 8, color, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
966 png_set_rows(pngs, pngi, rows);
967 png_write_png(pngs, pngi, PNG_TRANSFORM_IDENTITY, NULL);
968 png_destroy_write_struct(&pngs, &pngi);
974 printf("Saved %dx%d PNG image to %s\n", image->w, image->h, fn);