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 unsigned round_to_pot(unsigned);
66 void *alloc_image_data(size_t, size_t);
67 int init_font(Font *, FT_Face, unsigned, unsigned, int);
68 int render_grid(Font *, unsigned, unsigned, unsigned, int);
69 int render_packed(Font *);
70 int save_defs(const char *, const Font *);
71 int save_png(const char *, const Image *, char);
75 int main(int argc, char **argv)
96 char *out_fn = "font.png";
107 while((i = getopt(argc, argv, "r:s:l:c:o:atvh?ed:pi")) != -1)
114 if(!strcmp(optarg, "all"))
121 if(!isdigit(optarg[0]))
125 temp = strtol(optarg, &ptr, 0);
126 if(ptr[0]!=',' || !isdigit(ptr[1]))
131 printf("Not a valid range: %s\n", optarg);
137 end = strtol(ptr+1, NULL, 0);
142 size = strtol(optarg, NULL, 0);
145 cpl = strtol(optarg, NULL, 0);
148 if(!strcmp(optarg, "auto"))
153 else if(!strcmp(optarg, "autorect"))
160 cellw = strtol(optarg, &ptr, 0);
161 if(ptr[0]=='x' && isdigit(ptr[1]))
162 cellh = strtol(ptr+1, NULL, 0);
197 if(!strcmp(out_fn, "-"))
208 err = FT_Init_FreeType(&freetype);
211 fprintf(stderr, "Couldn't initialize FreeType library\n");
215 err = FT_New_Face(freetype, fn, 0, &face);
218 fprintf(stderr, "Couldn't load font file\n");
219 if(err==FT_Err_Unknown_File_Format)
220 fprintf(stderr, "Unknown file format\n");
226 const char *name = FT_Get_Postscript_Name(face);
227 printf("Font name: %s\n", name);
228 printf("Glyphs: %ld\n", face->num_glyphs);
231 err = FT_Set_Pixel_Sizes(face, 0, size);
234 fprintf(stderr, "Couldn't set size\n");
239 err = init_font(&font, face, begin, end, autohinter);
244 err = render_packed(&font);
246 err = render_grid(&font, cellw, cellh, cpl, seq);
252 for(i=0; (unsigned)i<font.image.w*font.image.h; ++i)
253 font.image.data[i] = 255-font.image.data[i];
255 err = save_png(out_fn, &font.image, alpha);
260 save_defs(def_fn, &font);
262 for(i=0; (unsigned)i<font.n_glyphs; ++i)
263 free(font.glyphs[i].image.data);
266 free(font.image.data);
269 FT_Done_FreeType(freetype);
276 printf("ttf2png 1.0 - True Type Font to PNG converter\n"
277 "Copyright (c) 2004-2008 Mikko Rasa, Mikkosoft Productions\n"
278 "Distributed under the GNU General Public License\n\n");
280 printf("Usage: ttf2png [options] <TTF file>\n\n");
282 printf("Accepted options (default values in [brackets])\n"
283 " -r Range of characters to convert [0,255]\n"
284 " -s Font size to use, in pixels [10]\n"
285 " -l Number of characters to put in one line [auto]\n"
286 " -c Character cell size, in pixels [auto]\n"
287 " -o Output file name (or - for stdout) [font.png]\n");
288 printf(" -a Force autohinter\n"
289 " -t Render glyphs to alpha channel\n"
290 " -i Invert colors of the glyphs\n"
291 " -v Increase the level of verbosity\n"
292 " -e Use cells in sequence, without gaps\n"
293 " -p Pack the glyphs tightly instead of in a grid\n"
294 " -d File name for writing glyph definitions\n"
295 " -h Print this message\n");
298 unsigned round_to_pot(unsigned n)
310 void *alloc_image_data(size_t a, size_t b)
314 /* Carry out the multiplication manually so we can check for overflow. */
323 fprintf(stderr, "Cannot allocate %lu kbytes of memory for image\n", (unsigned long)(c/1024*b));
330 fprintf(stderr, "Cannot allocate %lu kbytes of memory for image\n", (unsigned long)(a/1024*b));
334 int init_font(Font *font, FT_Face face, unsigned first, unsigned last, int autohinter)
339 font->ascent = (face->size->metrics.ascender+63)>>6;
340 font->descent = (face->size->metrics.descender+63)>>6;
344 printf("Ascent: %d\n", font->ascent);
345 printf("Descent: %d\n", font->descent);
350 for(i=first; i<=last; ++i)
353 FT_Bitmap *bmp = &face->glyph->bitmap;
358 n = FT_Get_Char_Index(face, i);
363 flags |= FT_LOAD_FORCE_AUTOHINT;
364 FT_Load_Glyph(face, n, flags);
365 FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
368 printf(" Char %u: glyph %u, size %dx%d\n", i, n, bmp->width, bmp->rows);
370 if(bmp->pixel_mode!=FT_PIXEL_MODE_GRAY)
372 fprintf(stderr, "Warning: Glyph %u skipped, not grayscale\n", n);
376 if(font->n_glyphs>=size)
379 font->glyphs = (Glyph *)realloc(font->glyphs, size*sizeof(Glyph));
382 glyph = &font->glyphs[font->n_glyphs++];
385 glyph->image.w = bmp->width;
386 glyph->image.h = bmp->rows;
387 glyph->image.data = (char *)malloc(bmp->width*bmp->rows);
388 if(!glyph->image.data)
390 fprintf(stderr, "Cannot allocate %d bytes of memory for glyph\n", bmp->width*bmp->rows);
393 glyph->offset_x = face->glyph->bitmap_left;
394 glyph->offset_y = face->glyph->bitmap_top-bmp->rows;
395 glyph->advance = (int)(face->glyph->advance.x+32)/64;
397 /* Copy the glyph image since FreeType uses a global buffer, which would
398 be overwritten by the next glyph. Negative pitch means the scanlines
399 start from the bottom. */
402 for(y=0; y<bmp->rows; ++y) for(x=0; x<bmp->width; ++x)
403 glyph->image.data[x+(glyph->image.h-1-y)*glyph->image.w] = bmp->buffer[x-y*bmp->pitch];
407 for(y=0; y<bmp->rows; ++y) for(x=0; x<bmp->width; ++x)
408 glyph->image.data[x+y*glyph->image.w] = bmp->buffer[x+y*bmp->pitch];
413 printf("Loaded %u glyphs\n", font->n_glyphs);
417 font->kerning = NULL;
418 for(i=0; i<font->n_glyphs; ++i) for(j=0; j<font->n_glyphs; ++j)
422 FT_Get_Kerning(face, font->glyphs[i].index, font->glyphs[j].index, FT_KERNING_DEFAULT, &kerning);
424 /* FreeType documentation says that vertical kerning is practically
425 never used, so we ignore it. */
430 if(font->n_kerning>=size)
433 font->kerning = (Kerning *)realloc(font->kerning, size*sizeof(Kerning));
436 kern = &font->kerning[font->n_kerning++];
437 kern->left_code = font->glyphs[i].code;
438 kern->right_code = font->glyphs[j].code;
439 kern->distance = kerning.x/64;
444 printf("Loaded %d kerning pairs\n", font->n_kerning);
449 int render_grid(Font *font, unsigned cellw, unsigned cellh, unsigned cpl, int seq)
452 int top = 0, bot = 0;
453 unsigned first, last;
454 unsigned maxw = 0, maxh = 0;
456 /* Find extremes of the glyph images. */
457 for(i=0; i<font->n_glyphs; ++i)
461 y = font->glyphs[i].offset_y+font->glyphs[i].image.h;
464 if(font->glyphs[i].offset_y<bot)
465 bot = font->glyphs[i].offset_y;
466 if(font->glyphs[i].image.w>maxw)
467 maxw = font->glyphs[i].image.w;
468 if(font->glyphs[i].image.h>maxh)
469 maxh = font->glyphs[i].image.h;
474 /* Establish a large enough cell to hold all glyphs in the range. */
475 int square = (cellh==cellw);
489 printf("Max size: %u x %u\n", maxw, maxh);
490 printf("Y range: [%d %d]\n", bot, top);
491 printf("Cell size: %u x %u\n", cellw, cellh);
492 if(maxw>cellw || (unsigned)(top-bot)>cellh)
493 fprintf(stderr, "Warning: character size exceeds cell size\n");
498 /* Determine number of characters per line, trying to fit all the glyphs
499 in a square image. */
503 if(cpl>0 && font->n_glyphs/cpl*cellh<=cpl*cellw)
508 first = font->glyphs[0].code;
511 last = font->glyphs[font->n_glyphs-1].code;
513 font->image.w = round_to_pot(cpl*cellw);
515 font->image.h = round_to_pot((font->n_glyphs+cpl-1)/cpl*cellh);
517 font->image.h = round_to_pot((last-first+cpl)/cpl*cellh);
519 font->image.data = (char *)alloc_image_data(font->image.w, font->image.h);
520 if(!font->image.data)
522 memset(font->image.data, 255, font->image.w*font->image.h);
524 for(i=0; i<font->n_glyphs; ++i)
530 glyph = &font->glyphs[i];
535 ci = glyph->code-first;
540 if(cellw>glyph->image.w)
541 cx += (cellw-glyph->image.w)/2;
542 cy += top-glyph->offset_y-glyph->image.h;
547 for(y=0; y<glyph->image.h; ++y) for(x=0; x<glyph->image.w; ++x)
549 if(cx+x>=font->image.w || cy+y>=font->image.h)
551 font->image.data[cx+x+(cy+y)*font->image.w] = 255-glyph->image.data[x+y*glyph->image.w];
558 int render_packed(Font *font)
563 unsigned *used_pixels;
567 /* Compute the total area occupied by glyphs and padding. */
568 for(i=0; i<font->n_glyphs; ++i)
570 size_t a = area+(font->glyphs[i].image.w+1)*(font->glyphs[i].image.h+1);
573 fprintf(stderr, "Overflow in counting total glyph area\n");
579 /* Find an image size that's no higher than wide, allowing for some
580 imperfections in the packing. */
581 for(font->image.w=1;; font->image.w<<=1)
583 font->image.h = (area*5/4)/font->image.w;
584 if(font->image.h<=font->image.w)
587 font->image.h = round_to_pot(font->image.h);
589 /* Allocate arrays for storing the image and keeping track of used pixels and
590 glyphs. Since glyphs are rectangular and the image is filled starting from
591 the top, it's enough to track the number of used pixels at the top of each
593 font->image.data = (char *)alloc_image_data(font->image.w, font->image.h);
594 if(!font->image.data)
596 memset(font->image.data, 255, font->image.w*font->image.h);
597 used_pixels = (unsigned *)malloc(font->image.w*sizeof(unsigned));
598 memset(used_pixels, 0, font->image.w*sizeof(unsigned));
599 used_glyphs = (char *)malloc(font->n_glyphs);
600 memset(used_glyphs, 0, font->n_glyphs);
602 for(cy=0; cy<font->image.h;)
607 unsigned best_score = 0;
608 unsigned target_h = 0;
610 /* Find the leftmost free pixel on this row. Also record the lowest extent of glyphs
611 to the left of the free position. */
612 for(; (cx<font->image.w && used_pixels[cx]>cy); ++cx)
613 if(used_pixels[cx]-cy-1>target_h)
614 target_h = used_pixels[cx]-cy-1;
616 if(cx>=font->image.w)
623 /* Count the free pixel at this position. */
624 for(w=0; (cx+w<font->image.w && used_pixels[cx+w]<=cy); ++w) ;
626 /* Find a suitable glyph to put here. */
627 for(i=0; i<font->n_glyphs; ++i)
631 g = &font->glyphs[i];
632 if(!used_glyphs[i] && g->image.w<=w)
636 /* Prefer glyphs that would reach exactly as low as the ones left
637 of here. This aims to create a straight edge at the bottom for
638 lining up further glyphs. */
639 score = g->image.h+1;
640 if(g->image.h==target_h)
659 used_glyphs[glyph-font->glyphs] = 1;
663 for(y=0; y<glyph->image.h; ++y) for(x=0; x<glyph->image.w; ++x)
665 if(cx+x>=font->image.w || cy+y>=font->image.h)
667 font->image.data[cx+x+(cy+y)*font->image.w] = 255-glyph->image.data[x+y*glyph->image.w];
669 for(x=0; x<glyph->image.w+2; ++x)
671 if(cx+x<1 || cx+x>font->image.w)
673 if(used_pixels[cx+x-1]<cy+glyph->image.h+1)
674 used_pixels[cx+x-1] = cy+glyph->image.h+1;
677 if(cy+glyph->image.h>used_h)
678 used_h = cy+glyph->image.h;
681 /* Trim the image to the actually used size, in case the original estimate
682 was too pessimistic. */
683 font->image.h = round_to_pot(used_h);
691 int save_defs(const char *fn, const Font *font)
696 out = fopen(fn, "w");
699 fprintf(stderr, "Couldn't open %s\n",fn);
703 fprintf(out, "# Image/font info:\n");
704 fprintf(out, "# width height size ascent descent\n");
705 fprintf(out, "font %d %d %d %d %d\n", font->image.w, font->image.h, font->size, font->ascent, font->descent);
706 fprintf(out, "\n# Glyph info:\n");
707 fprintf(out, "# code x y width height offset_x offset_y advance\n");
708 for(i=0; i<font->n_glyphs; ++i)
710 const Glyph *g = &font->glyphs[i];
711 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);
713 fprintf(out, "\n# Kerning info:\n");
714 fprintf(out, "# left right distance\n");
715 for(i=0; i<font->n_kerning; ++i)
717 const Kerning *k = &font->kerning[i];
718 fprintf(out, "kern %u %u %d\n", k->left_code, k->right_code, k->distance);
726 int save_png(const char *fn, const Image *image, char alpha)
740 out = fopen(fn, "wb");
743 fprintf(stderr, "Couldn't open %s\n",fn);
748 pngs = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
751 fprintf(stderr, "Error writing PNG file\n");
754 pngi = png_create_info_struct(pngs);
757 png_destroy_write_struct(&pngs, NULL);
758 fprintf(stderr, "Error writing PNG file\n");
762 png_init_io(pngs, out);
763 rows = (png_byte **)malloc(image->h*sizeof(png_byte *));
766 data2 = (png_byte *)alloc_image_data(image->w*2, image->h);
769 for(i=0; i<image->w*image->h; ++i)
772 data2[i*2+1] = 255-image->data[i];
774 for(i=0; i<image->h; ++i)
775 rows[i] = (png_byte *)(data2+i*image->w*2);
776 color = PNG_COLOR_TYPE_GRAY_ALPHA;
780 for(i=0; i<image->h; ++i)
781 rows[i] = (png_byte *)(image->data+i*image->w);
782 color = PNG_COLOR_TYPE_GRAY;
784 png_set_IHDR(pngs, pngi, image->w, image->h, 8, color, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
785 png_set_rows(pngs, pngi, rows);
786 png_write_png(pngs, pngi, PNG_TRANSFORM_IDENTITY, NULL);
787 png_destroy_write_struct(&pngs, &pngi);
793 printf("Saved %dx%d PNG image to %s\n", image->w, image->h, fn);