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 unsigned round_to_pot(unsigned);
78 void *alloc_image_data(size_t, size_t);
79 int init_font(Font *, FT_Face, const Range *, unsigned, bool);
80 int init_glyphs(Font *, FT_Face, const Range *, bool);
81 int render_grid(Font *, unsigned, unsigned, unsigned, bool);
82 int render_packed(Font *, unsigned, unsigned);
83 int save_defs(const char *, const Font *);
84 int save_png(const char *, const Image *, char);
88 int main(int argc, char **argv)
92 unsigned n_ranges = 0;
103 unsigned padding = 1;
111 char *out_fn = "font.png";
122 while((i = getopt(argc, argv, "r:s:l:c:o:atvh?ed:pim:n:")) != -1)
127 ranges = (Range *)realloc(ranges, (++n_ranges)*sizeof(Range));
128 convert_code_point_range('r', &ranges[n_ranges-1]);
131 size = convert_numeric_option('s', 1);
134 cpl = convert_numeric_option('l', 1);
137 convert_size('c', &cellw, &cellh);
168 margin = convert_numeric_option('m', 0);
171 padding = convert_numeric_option('n', 0);
175 if(!strcmp(out_fn, "-"))
186 err = FT_Init_FreeType(&freetype);
189 fprintf(stderr, "Couldn't initialize FreeType library\n");
193 err = FT_New_Face(freetype, fn, 0, &face);
196 fprintf(stderr, "Couldn't load font file\n");
197 if(err==FT_Err_Unknown_File_Format)
198 fprintf(stderr, "Unknown file format\n");
204 const char *name = FT_Get_Postscript_Name(face);
205 printf("Font name: %s\n", name);
206 printf("Glyphs: %ld\n", face->num_glyphs);
209 err = FT_Set_Pixel_Sizes(face, 0, size);
212 fprintf(stderr, "Couldn't set size\n");
217 err = init_font(&font, face, ranges, n_ranges, autohinter);
223 fprintf(stderr, "No glyphs found in the requested range\n");
228 err = render_packed(&font, margin, padding);
230 err = render_grid(&font, cellw, cellh, cpl, seq);
236 for(i=0; (unsigned)i<font.image.w*font.image.h; ++i)
237 font.image.data[i] = 255-font.image.data[i];
239 err = save_png(out_fn, &font.image, alpha);
244 save_defs(def_fn, &font);
246 for(i=0; (unsigned)i<font.n_glyphs; ++i)
247 free(font.glyphs[i].image.data);
250 free(font.image.data);
253 FT_Done_FreeType(freetype);
260 printf("ttf2png 1.1 - True Type Font to PNG converter\n"
261 "Copyright (c) 2004-2018 Mikko Rasa, Mikkosoft Productions\n"
262 "Distributed under the GNU General Public License\n\n");
264 printf("Usage: ttf2png [options] <TTF file>\n\n");
266 printf("Accepted options (default values in [brackets])\n"
267 " -r Range of code points to convert [0,255]\n"
268 " -s Font size to use, in pixels [10]\n"
269 " -l Number of glyphs to put in one line [auto]\n"
270 " -c Glyph cell size, in pixels (grid mode only) [auto]\n"
271 " -o Output file name (or - for stdout) [font.png]\n");
272 printf(" -a Force autohinter\n"
273 " -t Render glyphs to alpha channel\n"
274 " -i Invert colors of the glyphs\n"
275 " -v Increase the level of verbosity\n"
276 " -e Use cells in sequence, without gaps (grid mode only)\n"
277 " -p Pack the glyphs tightly instead of in a grid\n"
278 " -m Margin around image edges (packed mode only) [0]\n"
279 " -n Padding between glyphs (packed mode only) [1]\n"
280 " -d File name for writing glyph definitions\n"
281 " -h Print this message\n");
284 int convert_numeric_option(char opt, int min_value)
289 value = strtol(optarg, &ptr, 0);
290 if(value<min_value || *ptr)
292 printf("Invalid option argument in -%c %s\n", opt, optarg);
299 void convert_code_point_range(char opt, Range *range)
304 if(!strcmp(optarg, "all"))
307 range->last = 0x10FFFF;
311 value = str_to_code_point(optarg, &ptr);
312 if(value>0 && *ptr==',')
314 range->first = value;
315 value = str_to_code_point(ptr+1, &ptr);
323 printf("Invalid option argument in -%c %s\n", opt, optarg);
327 unsigned str_to_code_point(const char *nptr, char **endptr)
329 if(nptr[0]=='U' && nptr[1]=='+')
330 return strtoul(nptr+2, endptr, 16);
331 else if(nptr[0]&0x80)
338 *endptr = (char *)nptr;
340 for(bytes=1; (bytes<4 && (nptr[0]&(0x80>>bytes))); ++bytes)
341 if((nptr[bytes]&0xC0)!=0x80)
346 code = nptr[0]&(0x3F>>bytes);
347 for(i=1; i<bytes; ++i)
348 code = (code<<6)|(nptr[i]&0x3F);
351 *endptr = (char *)nptr+bytes;
355 else if(isdigit(nptr[0]))
356 return strtoul(nptr, endptr, 0);
360 *endptr = (char *)nptr+1;
365 void convert_size(char opt, unsigned *width, unsigned *height)
370 if(!strcmp(optarg, "auto"))
376 else if(!strcmp(optarg, "autorect"))
383 value = strtol(optarg, &ptr, 0);
389 value = strtol(ptr+1, &ptr, 0);
403 printf("Invalid option argument in -%c %s\n", opt, optarg);
407 unsigned round_to_pot(unsigned n)
419 void *alloc_image_data(size_t a, size_t b)
423 /* Carry out the multiplication manually so we can check for overflow. */
432 fprintf(stderr, "Cannot allocate %lu kbytes of memory for image\n", (unsigned long)(c/1024*b));
439 fprintf(stderr, "Cannot allocate %lu kbytes of memory for image\n", (unsigned long)(a/1024*b));
443 int init_font(Font *font, FT_Face face, const Range *ranges, unsigned n_ranges, bool autohinter)
448 font->ascent = (face->size->metrics.ascender+63)>>6;
449 font->descent = (face->size->metrics.descender+63)>>6;
453 printf("Ascent: %d\n", font->ascent);
454 printf("Descent: %d\n", font->descent);
459 for(i=0; i<n_ranges; ++i)
460 if(init_glyphs(font, face, &ranges[i], autohinter))
464 printf("Loaded %u glyphs\n", font->n_glyphs);
467 font->kerning = NULL;
468 for(i=0; i<font->n_glyphs; ++i) for(j=0; j<font->n_glyphs; ++j)
472 FT_Get_Kerning(face, font->glyphs[i].index, font->glyphs[j].index, FT_KERNING_DEFAULT, &kerning);
474 /* FreeType documentation says that vertical kerning is practically
475 never used, so we ignore it. */
480 if(font->n_kerning>=size)
483 font->kerning = (Kerning *)realloc(font->kerning, size*sizeof(Kerning));
486 kern = &font->kerning[font->n_kerning++];
487 kern->left_code = font->glyphs[i].code;
488 kern->right_code = font->glyphs[j].code;
489 kern->distance = kerning.x/64;
494 printf("Loaded %d kerning pairs\n", font->n_kerning);
499 int init_glyphs(Font *font, FT_Face face, const Range *range, bool autohinter)
502 unsigned size = font->n_glyphs;
504 for(i=range->first; i<=range->last; ++i)
507 FT_Bitmap *bmp = &face->glyph->bitmap;
512 n = FT_Get_Char_Index(face, i);
517 flags |= FT_LOAD_FORCE_AUTOHINT;
518 FT_Load_Glyph(face, n, flags);
519 FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
523 printf(" Code point U+%04X", i);
524 if(i>=0x20 && i<0x7F)
526 else if(i>=0xA0 && i<=0x10FFFF)
531 for(bytes=2; i>>(1+bytes*5); ++bytes) ;
532 for(j=0; j<bytes; ++j)
533 utf8[j] = 0x80 | ((i>>((bytes-j-1)*6))&0x3F);
534 utf8[0] |= 0xF0<<(4-bytes);
537 printf(" (%s)", utf8);
539 printf(": glyph %u, size %dx%d\n", n, bmp->width, bmp->rows);
542 if(bmp->pixel_mode!=FT_PIXEL_MODE_GRAY)
544 fprintf(stderr, "Warning: Glyph %u skipped, not grayscale\n", n);
548 if(font->n_glyphs>=size)
551 font->glyphs = (Glyph *)realloc(font->glyphs, size*sizeof(Glyph));
554 glyph = &font->glyphs[font->n_glyphs++];
557 glyph->image.w = bmp->width;
558 glyph->image.h = bmp->rows;
559 glyph->image.data = (char *)malloc(bmp->width*bmp->rows);
560 if(!glyph->image.data)
562 fprintf(stderr, "Cannot allocate %d bytes of memory for glyph\n", bmp->width*bmp->rows);
565 glyph->offset_x = face->glyph->bitmap_left;
566 glyph->offset_y = face->glyph->bitmap_top-bmp->rows;
567 glyph->advance = (int)(face->glyph->advance.x+32)/64;
569 /* Copy the glyph image since FreeType uses a global buffer, which would
570 be overwritten by the next glyph. Negative pitch means the scanlines
571 start from the bottom. */
574 for(y=0; y<bmp->rows; ++y) for(x=0; x<bmp->width; ++x)
575 glyph->image.data[x+(glyph->image.h-1-y)*glyph->image.w] = bmp->buffer[x-y*bmp->pitch];
579 for(y=0; y<bmp->rows; ++y) for(x=0; x<bmp->width; ++x)
580 glyph->image.data[x+y*glyph->image.w] = bmp->buffer[x+y*bmp->pitch];
587 int render_grid(Font *font, unsigned cellw, unsigned cellh, unsigned cpl, bool seq)
590 int top = 0, bot = 0;
591 unsigned first, last;
592 unsigned maxw = 0, maxh = 0;
594 /* Find extremes of the glyph images. */
595 for(i=0; i<font->n_glyphs; ++i)
599 y = font->glyphs[i].offset_y+font->glyphs[i].image.h;
602 if(font->glyphs[i].offset_y<bot)
603 bot = font->glyphs[i].offset_y;
604 if(font->glyphs[i].image.w>maxw)
605 maxw = font->glyphs[i].image.w;
606 if(font->glyphs[i].image.h>maxh)
607 maxh = font->glyphs[i].image.h;
612 /* Establish a large enough cell to hold all glyphs in the range. */
613 int square = (cellh==cellw);
627 printf("Max size: %u x %u\n", maxw, maxh);
628 printf("Y range: [%d %d]\n", bot, top);
629 printf("Cell size: %u x %u\n", cellw, cellh);
630 if(maxw>cellw || (unsigned)(top-bot)>cellh)
631 fprintf(stderr, "Warning: character size exceeds cell size\n");
636 /* Determine number of characters per line, trying to fit all the glyphs
637 in a square image. */
641 if(cpl>0 && font->n_glyphs/cpl*cellh<=cpl*cellw)
646 first = font->glyphs[0].code;
649 last = font->glyphs[font->n_glyphs-1].code;
651 font->image.w = round_to_pot(cpl*cellw);
653 font->image.h = round_to_pot((font->n_glyphs+cpl-1)/cpl*cellh);
655 font->image.h = round_to_pot((last-first+cpl)/cpl*cellh);
657 font->image.data = (char *)alloc_image_data(font->image.w, font->image.h);
658 if(!font->image.data)
660 memset(font->image.data, 255, font->image.w*font->image.h);
662 for(i=0; i<font->n_glyphs; ++i)
668 glyph = &font->glyphs[i];
673 ci = glyph->code-first;
678 if(cellw>glyph->image.w)
679 cx += (cellw-glyph->image.w)/2;
680 cy += top-glyph->offset_y-glyph->image.h;
685 for(y=0; y<glyph->image.h; ++y) for(x=0; x<glyph->image.w; ++x)
687 if(cx+x>=font->image.w || cy+y>=font->image.h)
689 font->image.data[cx+x+(cy+y)*font->image.w] = 255-glyph->image.data[x+y*glyph->image.w];
696 int render_packed(Font *font, unsigned margin, unsigned padding)
701 unsigned *used_pixels;
702 unsigned cx = margin, cy;
705 /* Compute the total area occupied by glyphs and padding. */
706 for(i=0; i<font->n_glyphs; ++i)
708 size_t a = area+(font->glyphs[i].image.w+padding)*(font->glyphs[i].image.h+padding);
711 fprintf(stderr, "Overflow in counting total glyph area\n");
717 /* Find an image size that's no higher than wide, allowing for some
718 imperfections in the packing. */
719 for(font->image.w=1;; font->image.w<<=1)
721 if(font->image.w<=margin*2)
723 font->image.h = (area*5/4)/(font->image.w-margin*2)+margin*2;
724 if(font->image.h<=font->image.w)
727 font->image.h = round_to_pot(font->image.h);
729 /* Allocate arrays for storing the image and keeping track of used pixels and
730 glyphs. Since glyphs are rectangular and the image is filled starting from
731 the top, it's enough to track the number of used pixels at the top of each
733 font->image.data = (char *)alloc_image_data(font->image.w, font->image.h);
734 if(!font->image.data)
736 memset(font->image.data, 255, font->image.w*font->image.h);
737 used_pixels = (unsigned *)malloc(font->image.w*sizeof(unsigned));
738 memset(used_pixels, 0, font->image.w*sizeof(unsigned));
739 used_glyphs = (char *)malloc(font->n_glyphs);
740 memset(used_glyphs, 0, font->n_glyphs);
742 for(cy=margin; cy+margin<font->image.h;)
747 unsigned best_score = 0;
748 unsigned target_h = 0;
750 /* Find the leftmost free pixel on this row. Also record the lowest
751 extent of glyphs to the left of the free position. */
752 for(; (cx+margin<font->image.w && used_pixels[cx]>cy); ++cx)
753 if(used_pixels[cx]-cy-padding>target_h)
754 target_h = used_pixels[cx]-cy-padding;
756 if(cx+margin>=font->image.w)
763 /* Count the free pixel at this position. */
764 for(w=0; (cx+w+margin<font->image.w && used_pixels[cx+w]<=cy); ++w) ;
766 /* Find a suitable glyph to put here. */
767 for(i=0; i<font->n_glyphs; ++i)
771 g = &font->glyphs[i];
772 if(!used_glyphs[i] && g->image.w<=w)
776 /* Prefer glyphs that would reach exactly as low as the ones left
777 of here. This aims to create a straight edge at the bottom for
778 lining up further glyphs. */
779 score = g->image.h+padding;
780 if(g->image.h==target_h)
799 used_glyphs[glyph-font->glyphs] = 1;
803 for(y=0; y<glyph->image.h; ++y) for(x=0; x<glyph->image.w; ++x)
805 if(cx+x>=font->image.w || cy+y>=font->image.h)
807 font->image.data[cx+x+(cy+y)*font->image.w] = 255-glyph->image.data[x+y*glyph->image.w];
809 for(x=0; x<glyph->image.w+2*padding; ++x)
811 if(cx+x<padding || cx+x>=font->image.w+padding)
813 if(used_pixels[cx+x-padding]<cy+glyph->image.h+padding)
814 used_pixels[cx+x-padding] = cy+glyph->image.h+padding;
817 if(cy+glyph->image.h+margin>used_h)
818 used_h = cy+glyph->image.h+margin;
821 /* Trim the image to the actually used size, in case the original estimate
822 was too pessimistic. */
823 font->image.h = round_to_pot(used_h);
831 int save_defs(const char *fn, const Font *font)
836 out = fopen(fn, "w");
839 fprintf(stderr, "Couldn't open %s\n",fn);
843 fprintf(out, "# Image/font info:\n");
844 fprintf(out, "# width height size ascent descent\n");
845 fprintf(out, "font %d %d %d %d %d\n", font->image.w, font->image.h, font->size, font->ascent, font->descent);
846 fprintf(out, "\n# Glyph info:\n");
847 fprintf(out, "# code x y width height offset_x offset_y advance\n");
848 for(i=0; i<font->n_glyphs; ++i)
850 const Glyph *g = &font->glyphs[i];
851 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);
853 fprintf(out, "\n# Kerning info:\n");
854 fprintf(out, "# left right distance\n");
855 for(i=0; i<font->n_kerning; ++i)
857 const Kerning *k = &font->kerning[i];
858 fprintf(out, "kern %u %u %d\n", k->left_code, k->right_code, k->distance);
866 int save_png(const char *fn, const Image *image, char alpha)
880 out = fopen(fn, "wb");
883 fprintf(stderr, "Couldn't open %s\n",fn);
888 pngs = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
891 fprintf(stderr, "Error writing PNG file\n");
894 pngi = png_create_info_struct(pngs);
897 png_destroy_write_struct(&pngs, NULL);
898 fprintf(stderr, "Error writing PNG file\n");
902 png_init_io(pngs, out);
903 rows = (png_byte **)malloc(image->h*sizeof(png_byte *));
906 data2 = (png_byte *)alloc_image_data(image->w*2, image->h);
909 for(i=0; i<image->w*image->h; ++i)
912 data2[i*2+1] = 255-image->data[i];
914 for(i=0; i<image->h; ++i)
915 rows[i] = (png_byte *)(data2+i*image->w*2);
916 color = PNG_COLOR_TYPE_GRAY_ALPHA;
920 for(i=0; i<image->h; ++i)
921 rows[i] = (png_byte *)(image->data+i*image->w);
922 color = PNG_COLOR_TYPE_GRAY;
924 png_set_IHDR(pngs, pngi, image->w, image->h, 8, color, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
925 png_set_rows(pngs, pngi, rows);
926 png_write_png(pngs, pngi, PNG_TRANSFORM_IDENTITY, NULL);
927 png_destroy_write_struct(&pngs, &pngi);
933 printf("Saved %dx%d PNG image to %s\n", image->w, image->h, fn);