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
65 int convert_numeric_option(char, int);
66 void convert_code_point_range(char, int *, int *);
67 void convert_size(char, int *, int *);
68 unsigned round_to_pot(unsigned);
69 void *alloc_image_data(size_t, size_t);
70 int init_font(Font *, FT_Face, unsigned, unsigned, int);
71 int render_grid(Font *, unsigned, unsigned, unsigned, int);
72 int render_packed(Font *, unsigned, unsigned);
73 int save_defs(const char *, const Font *);
74 int save_png(const char *, const Image *, char);
78 int main(int argc, char **argv)
101 char *out_fn = "font.png";
112 while((i = getopt(argc, argv, "r:s:l:c:o:atvh?ed:pim:n:")) != -1)
117 convert_code_point_range('r', &begin, &end);
120 size = convert_numeric_option('s', 1);
123 cpl = convert_numeric_option('l', 1);
126 convert_size('c', &cellw, &cellh);
157 margin = convert_numeric_option('m', 0);
160 padding = convert_numeric_option('n', 0);
164 if(!strcmp(out_fn, "-"))
175 err = FT_Init_FreeType(&freetype);
178 fprintf(stderr, "Couldn't initialize FreeType library\n");
182 err = FT_New_Face(freetype, fn, 0, &face);
185 fprintf(stderr, "Couldn't load font file\n");
186 if(err==FT_Err_Unknown_File_Format)
187 fprintf(stderr, "Unknown file format\n");
193 const char *name = FT_Get_Postscript_Name(face);
194 printf("Font name: %s\n", name);
195 printf("Glyphs: %ld\n", face->num_glyphs);
198 err = FT_Set_Pixel_Sizes(face, 0, size);
201 fprintf(stderr, "Couldn't set size\n");
206 err = init_font(&font, face, begin, end, autohinter);
211 err = render_packed(&font, margin, padding);
213 err = render_grid(&font, cellw, cellh, cpl, seq);
219 for(i=0; (unsigned)i<font.image.w*font.image.h; ++i)
220 font.image.data[i] = 255-font.image.data[i];
222 err = save_png(out_fn, &font.image, alpha);
227 save_defs(def_fn, &font);
229 for(i=0; (unsigned)i<font.n_glyphs; ++i)
230 free(font.glyphs[i].image.data);
233 free(font.image.data);
236 FT_Done_FreeType(freetype);
243 printf("ttf2png 1.1 - True Type Font to PNG converter\n"
244 "Copyright (c) 2004-2018 Mikko Rasa, Mikkosoft Productions\n"
245 "Distributed under the GNU General Public License\n\n");
247 printf("Usage: ttf2png [options] <TTF file>\n\n");
249 printf("Accepted options (default values in [brackets])\n"
250 " -r Range of code points to convert [0,255]\n"
251 " -s Font size to use, in pixels [10]\n"
252 " -l Number of glyphs to put in one line [auto]\n"
253 " -c Glyph cell size, in pixels (grid mode only) [auto]\n"
254 " -o Output file name (or - for stdout) [font.png]\n");
255 printf(" -a Force autohinter\n"
256 " -t Render glyphs to alpha channel\n"
257 " -i Invert colors of the glyphs\n"
258 " -v Increase the level of verbosity\n"
259 " -e Use cells in sequence, without gaps (grid mode only)\n"
260 " -p Pack the glyphs tightly instead of in a grid\n"
261 " -m Margin around image edges (packed mode only) [0]\n"
262 " -n Padding between glyphs (packed mode only) [1]\n"
263 " -d File name for writing glyph definitions\n"
264 " -h Print this message\n");
267 int convert_numeric_option(char opt, int min_value)
272 value = strtol(optarg, &ptr, 0);
273 if(value<min_value || *ptr)
275 printf("Invalid option argument in -%c %s\n", opt, optarg);
282 void convert_code_point_range(char opt, int *begin, int *end)
286 if(!strcmp(optarg, "all"))
293 *begin = strtol(optarg, &ptr, 0);
294 if(*begin>0 && *ptr==',')
296 *end = strtol(ptr+1, &ptr, 0);
301 printf("Invalid option argument in -%c %s\n", opt, optarg);
305 void convert_size(char opt, int *width, int *height)
309 if(!strcmp(optarg, "auto"))
315 else if(!strcmp(optarg, "autorect"))
322 *width = strtol(optarg, &ptr, 0);
327 *height = strtol(ptr+1, &ptr, 0);
328 if(*height>0 && !*ptr)
338 printf("Invalid option argument in -%c %s\n", opt, optarg);
342 unsigned round_to_pot(unsigned n)
354 void *alloc_image_data(size_t a, size_t b)
358 /* Carry out the multiplication manually so we can check for overflow. */
367 fprintf(stderr, "Cannot allocate %lu kbytes of memory for image\n", (unsigned long)(c/1024*b));
374 fprintf(stderr, "Cannot allocate %lu kbytes of memory for image\n", (unsigned long)(a/1024*b));
378 int init_font(Font *font, FT_Face face, unsigned first, unsigned last, int autohinter)
383 font->ascent = (face->size->metrics.ascender+63)>>6;
384 font->descent = (face->size->metrics.descender+63)>>6;
388 printf("Ascent: %d\n", font->ascent);
389 printf("Descent: %d\n", font->descent);
394 for(i=first; i<=last; ++i)
397 FT_Bitmap *bmp = &face->glyph->bitmap;
402 n = FT_Get_Char_Index(face, i);
407 flags |= FT_LOAD_FORCE_AUTOHINT;
408 FT_Load_Glyph(face, n, flags);
409 FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
412 printf(" Char %u: glyph %u, size %dx%d\n", i, n, bmp->width, bmp->rows);
414 if(bmp->pixel_mode!=FT_PIXEL_MODE_GRAY)
416 fprintf(stderr, "Warning: Glyph %u skipped, not grayscale\n", n);
420 if(font->n_glyphs>=size)
423 font->glyphs = (Glyph *)realloc(font->glyphs, size*sizeof(Glyph));
426 glyph = &font->glyphs[font->n_glyphs++];
429 glyph->image.w = bmp->width;
430 glyph->image.h = bmp->rows;
431 glyph->image.data = (char *)malloc(bmp->width*bmp->rows);
432 if(!glyph->image.data)
434 fprintf(stderr, "Cannot allocate %d bytes of memory for glyph\n", bmp->width*bmp->rows);
437 glyph->offset_x = face->glyph->bitmap_left;
438 glyph->offset_y = face->glyph->bitmap_top-bmp->rows;
439 glyph->advance = (int)(face->glyph->advance.x+32)/64;
441 /* Copy the glyph image since FreeType uses a global buffer, which would
442 be overwritten by the next glyph. Negative pitch means the scanlines
443 start from the bottom. */
446 for(y=0; y<bmp->rows; ++y) for(x=0; x<bmp->width; ++x)
447 glyph->image.data[x+(glyph->image.h-1-y)*glyph->image.w] = bmp->buffer[x-y*bmp->pitch];
451 for(y=0; y<bmp->rows; ++y) for(x=0; x<bmp->width; ++x)
452 glyph->image.data[x+y*glyph->image.w] = bmp->buffer[x+y*bmp->pitch];
457 printf("Loaded %u glyphs\n", font->n_glyphs);
461 font->kerning = NULL;
462 for(i=0; i<font->n_glyphs; ++i) for(j=0; j<font->n_glyphs; ++j)
466 FT_Get_Kerning(face, font->glyphs[i].index, font->glyphs[j].index, FT_KERNING_DEFAULT, &kerning);
468 /* FreeType documentation says that vertical kerning is practically
469 never used, so we ignore it. */
474 if(font->n_kerning>=size)
477 font->kerning = (Kerning *)realloc(font->kerning, size*sizeof(Kerning));
480 kern = &font->kerning[font->n_kerning++];
481 kern->left_code = font->glyphs[i].code;
482 kern->right_code = font->glyphs[j].code;
483 kern->distance = kerning.x/64;
488 printf("Loaded %d kerning pairs\n", font->n_kerning);
493 int render_grid(Font *font, unsigned cellw, unsigned cellh, unsigned cpl, int seq)
496 int top = 0, bot = 0;
497 unsigned first, last;
498 unsigned maxw = 0, maxh = 0;
500 /* Find extremes of the glyph images. */
501 for(i=0; i<font->n_glyphs; ++i)
505 y = font->glyphs[i].offset_y+font->glyphs[i].image.h;
508 if(font->glyphs[i].offset_y<bot)
509 bot = font->glyphs[i].offset_y;
510 if(font->glyphs[i].image.w>maxw)
511 maxw = font->glyphs[i].image.w;
512 if(font->glyphs[i].image.h>maxh)
513 maxh = font->glyphs[i].image.h;
518 /* Establish a large enough cell to hold all glyphs in the range. */
519 int square = (cellh==cellw);
533 printf("Max size: %u x %u\n", maxw, maxh);
534 printf("Y range: [%d %d]\n", bot, top);
535 printf("Cell size: %u x %u\n", cellw, cellh);
536 if(maxw>cellw || (unsigned)(top-bot)>cellh)
537 fprintf(stderr, "Warning: character size exceeds cell size\n");
542 /* Determine number of characters per line, trying to fit all the glyphs
543 in a square image. */
547 if(cpl>0 && font->n_glyphs/cpl*cellh<=cpl*cellw)
552 first = font->glyphs[0].code;
555 last = font->glyphs[font->n_glyphs-1].code;
557 font->image.w = round_to_pot(cpl*cellw);
559 font->image.h = round_to_pot((font->n_glyphs+cpl-1)/cpl*cellh);
561 font->image.h = round_to_pot((last-first+cpl)/cpl*cellh);
563 font->image.data = (char *)alloc_image_data(font->image.w, font->image.h);
564 if(!font->image.data)
566 memset(font->image.data, 255, font->image.w*font->image.h);
568 for(i=0; i<font->n_glyphs; ++i)
574 glyph = &font->glyphs[i];
579 ci = glyph->code-first;
584 if(cellw>glyph->image.w)
585 cx += (cellw-glyph->image.w)/2;
586 cy += top-glyph->offset_y-glyph->image.h;
591 for(y=0; y<glyph->image.h; ++y) for(x=0; x<glyph->image.w; ++x)
593 if(cx+x>=font->image.w || cy+y>=font->image.h)
595 font->image.data[cx+x+(cy+y)*font->image.w] = 255-glyph->image.data[x+y*glyph->image.w];
602 int render_packed(Font *font, unsigned margin, unsigned padding)
607 unsigned *used_pixels;
608 unsigned cx = margin, cy;
611 /* Compute the total area occupied by glyphs and padding. */
612 for(i=0; i<font->n_glyphs; ++i)
614 size_t a = area+(font->glyphs[i].image.w+padding)*(font->glyphs[i].image.h+padding);
617 fprintf(stderr, "Overflow in counting total glyph area\n");
623 /* Find an image size that's no higher than wide, allowing for some
624 imperfections in the packing. */
625 for(font->image.w=1;; font->image.w<<=1)
627 if(font->image.w<=margin*2)
629 font->image.h = (area*5/4)/(font->image.w-margin*2)+margin*2;
630 if(font->image.h<=font->image.w)
633 font->image.h = round_to_pot(font->image.h);
635 /* Allocate arrays for storing the image and keeping track of used pixels and
636 glyphs. Since glyphs are rectangular and the image is filled starting from
637 the top, it's enough to track the number of used pixels at the top of each
639 font->image.data = (char *)alloc_image_data(font->image.w, font->image.h);
640 if(!font->image.data)
642 memset(font->image.data, 255, font->image.w*font->image.h);
643 used_pixels = (unsigned *)malloc(font->image.w*sizeof(unsigned));
644 memset(used_pixels, 0, font->image.w*sizeof(unsigned));
645 used_glyphs = (char *)malloc(font->n_glyphs);
646 memset(used_glyphs, 0, font->n_glyphs);
648 for(cy=margin; cy+margin<font->image.h;)
653 unsigned best_score = 0;
654 unsigned target_h = 0;
656 /* Find the leftmost free pixel on this row. Also record the lowest
657 extent of glyphs to the left of the free position. */
658 for(; (cx+margin<font->image.w && used_pixels[cx]>cy); ++cx)
659 if(used_pixels[cx]-cy-padding>target_h)
660 target_h = used_pixels[cx]-cy-padding;
662 if(cx+margin>=font->image.w)
669 /* Count the free pixel at this position. */
670 for(w=0; (cx+w+margin<font->image.w && used_pixels[cx+w]<=cy); ++w) ;
672 /* Find a suitable glyph to put here. */
673 for(i=0; i<font->n_glyphs; ++i)
677 g = &font->glyphs[i];
678 if(!used_glyphs[i] && g->image.w<=w)
682 /* Prefer glyphs that would reach exactly as low as the ones left
683 of here. This aims to create a straight edge at the bottom for
684 lining up further glyphs. */
685 score = g->image.h+padding;
686 if(g->image.h==target_h)
705 used_glyphs[glyph-font->glyphs] = 1;
709 for(y=0; y<glyph->image.h; ++y) for(x=0; x<glyph->image.w; ++x)
711 if(cx+x>=font->image.w || cy+y>=font->image.h)
713 font->image.data[cx+x+(cy+y)*font->image.w] = 255-glyph->image.data[x+y*glyph->image.w];
715 for(x=0; x<glyph->image.w+2*padding; ++x)
717 if(cx+x<padding || cx+x>=font->image.w+padding)
719 if(used_pixels[cx+x-padding]<cy+glyph->image.h+padding)
720 used_pixels[cx+x-padding] = cy+glyph->image.h+padding;
723 if(cy+glyph->image.h+margin>used_h)
724 used_h = cy+glyph->image.h+margin;
727 /* Trim the image to the actually used size, in case the original estimate
728 was too pessimistic. */
729 font->image.h = round_to_pot(used_h);
737 int save_defs(const char *fn, const Font *font)
742 out = fopen(fn, "w");
745 fprintf(stderr, "Couldn't open %s\n",fn);
749 fprintf(out, "# Image/font info:\n");
750 fprintf(out, "# width height size ascent descent\n");
751 fprintf(out, "font %d %d %d %d %d\n", font->image.w, font->image.h, font->size, font->ascent, font->descent);
752 fprintf(out, "\n# Glyph info:\n");
753 fprintf(out, "# code x y width height offset_x offset_y advance\n");
754 for(i=0; i<font->n_glyphs; ++i)
756 const Glyph *g = &font->glyphs[i];
757 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);
759 fprintf(out, "\n# Kerning info:\n");
760 fprintf(out, "# left right distance\n");
761 for(i=0; i<font->n_kerning; ++i)
763 const Kerning *k = &font->kerning[i];
764 fprintf(out, "kern %u %u %d\n", k->left_code, k->right_code, k->distance);
772 int save_png(const char *fn, const Image *image, char alpha)
786 out = fopen(fn, "wb");
789 fprintf(stderr, "Couldn't open %s\n",fn);
794 pngs = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
797 fprintf(stderr, "Error writing PNG file\n");
800 pngi = png_create_info_struct(pngs);
803 png_destroy_write_struct(&pngs, NULL);
804 fprintf(stderr, "Error writing PNG file\n");
808 png_init_io(pngs, out);
809 rows = (png_byte **)malloc(image->h*sizeof(png_byte *));
812 data2 = (png_byte *)alloc_image_data(image->w*2, image->h);
815 for(i=0; i<image->w*image->h; ++i)
818 data2[i*2+1] = 255-image->data[i];
820 for(i=0; i<image->h; ++i)
821 rows[i] = (png_byte *)(data2+i*image->w*2);
822 color = PNG_COLOR_TYPE_GRAY_ALPHA;
826 for(i=0; i<image->h; ++i)
827 rows[i] = (png_byte *)(image->data+i*image->w);
828 color = PNG_COLOR_TYPE_GRAY;
830 png_set_IHDR(pngs, pngi, image->w, image->h, 8, color, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
831 png_set_rows(pngs, pngi, rows);
832 png_write_png(pngs, pngi, PNG_TRANSFORM_IDENTITY, NULL);
833 png_destroy_write_struct(&pngs, &pngi);
839 printf("Saved %dx%d PNG image to %s\n", image->w, image->h, fn);