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
64 unsigned round_to_pot(unsigned);
66 void init_font(Font *, FT_Face, unsigned, unsigned, int);
67 void render_grid(Font *, unsigned, unsigned, unsigned, int);
68 void render_packed(Font *);
69 int save_defs(const char *, const Font *);
70 int save_png(const char *, const Image *, char);
74 int main(int argc, char **argv)
95 char *out_fn = "font.png";
106 while((i = getopt(argc, argv, "r:s:l:c:o:atvh?ed:pi")) != -1)
113 if(!strcmp(optarg, "all"))
120 if(!isdigit(optarg[0]))
124 temp = strtol(optarg, &ptr, 0);
125 if(ptr[0]!=',' || !isdigit(ptr[1]))
130 printf("Not a valid range: %s\n", optarg);
136 end = strtol(ptr+1, NULL, 0);
141 size = strtol(optarg, NULL, 0);
144 cpl = strtol(optarg, NULL, 0);
147 if(!strcmp(optarg, "auto"))
152 else if(!strcmp(optarg, "autorect"))
159 cellw = strtol(optarg, &ptr, 0);
160 if(ptr[0]=='x' && isdigit(ptr[1]))
161 cellh = strtol(ptr+1, NULL, 0);
194 if(!strcmp(out_fn, "-"))
205 err = FT_Init_FreeType(&freetype);
208 fprintf(stderr, "Couldn't initialize FreeType library\n");
212 err = FT_New_Face(freetype, fn, 0, &face);
215 fprintf(stderr, "Couldn't load font file\n");
216 if(err==FT_Err_Unknown_File_Format)
217 fprintf(stderr, "Unknown file format\n");
223 const char *name = FT_Get_Postscript_Name(face);
224 printf("Font name: %s\n", name);
225 printf("Glyphs: %ld\n", face->num_glyphs);
228 err = FT_Set_Pixel_Sizes(face, 0, size);
231 fprintf(stderr, "Couldn't set size\n");
236 init_font(&font, face, begin, end, autohinter);
238 render_packed(&font);
240 render_grid(&font, cellw, cellh, cpl, seq);
243 for(i=0; (unsigned)i<font.image.w*font.image.h; ++i)
244 font.image.data[i] = 255-font.image.data[i];
246 save_png(out_fn, &font.image, alpha);
248 save_defs(def_fn, &font);
250 for(i=0; (unsigned)i<font.n_glyphs; ++i)
251 free(font.glyphs[i].image.data);
253 free(font.image.data);
256 FT_Done_FreeType(freetype);
261 unsigned round_to_pot(unsigned n)
275 printf("ttf2png - True Type Font to PNG converter\n"
276 "Copyright (c) 2004-2008 Mikko Rasa, Mikkosoft Productions\n"
277 "Distributed under the GNU General Public License\n\n");
279 printf("Usage: ttf2png [options] <TTF file>\n\n");
281 printf("Accepted options (default values in [brackets])\n"
282 " -r Range of characters to convert [0,255]\n"
283 " -s Font size to use, in pixels [10]\n"
284 " -l Number of characters to put in one line [auto]\n"
285 " -c Character cell size, in pixels [auto]\n"
286 " -o Output file name (or - for stdout) [font.png]\n");
287 printf(" -a Force autohinter\n"
288 " -t Render glyphs to alpha channel\n"
289 " -i Invert colors of the glyphs\n"
290 " -v Increase the level of verbosity\n"
291 " -e Use cells in sequence, without gaps\n"
292 " -p Pack the glyphs tightly instead of in a grid\n"
293 " -d File name for writing glyph definitions\n"
294 " -h Print this message\n");
297 void init_font(Font *font, FT_Face face, unsigned first, unsigned last, int autohinter)
302 font->ascent = (face->size->metrics.ascender+63)>>6;
303 font->descent = (face->size->metrics.descender+63)>>6;
307 printf("Ascent: %d\n", font->ascent);
308 printf("Descent: %d\n", font->descent);
313 for(i=first; i<=last; ++i)
316 FT_Bitmap *bmp = &face->glyph->bitmap;
321 n = FT_Get_Char_Index(face, i);
326 flags |= FT_LOAD_FORCE_AUTOHINT;
327 FT_Load_Glyph(face, n, flags);
328 FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
331 printf(" Char %u: glyph %u, size %dx%d\n", i, n, bmp->width, bmp->rows);
333 if(bmp->pixel_mode!=FT_PIXEL_MODE_GRAY)
335 fprintf(stderr, "Warning: Glyph %u skipped, not grayscale\n", n);
339 if(font->n_glyphs>=size)
342 font->glyphs = (Glyph *)realloc(font->glyphs, size*sizeof(Glyph));
345 glyph = &font->glyphs[font->n_glyphs++];
348 glyph->image.w = bmp->width;
349 glyph->image.h = bmp->rows;
350 glyph->image.data = (char *)malloc(bmp->width*bmp->rows);
351 glyph->offset_x = face->glyph->bitmap_left;
352 glyph->offset_y = face->glyph->bitmap_top-bmp->rows;
353 glyph->advance = (int)(face->glyph->advance.x+32)/64;
355 /* Copy the glyph image since FreeType uses a global buffer, which would
356 be overwritten by the next glyph. Negative pitch means the scanlines
357 start from the bottom */
360 for(y=0; y<bmp->rows; ++y) for(x=0; x<bmp->width; ++x)
361 glyph->image.data[x+(glyph->image.h-1-y)*glyph->image.w] = bmp->buffer[x-y*bmp->pitch];
365 for(y=0; y<bmp->rows; ++y) for(x=0; x<bmp->width; ++x)
366 glyph->image.data[x+y*glyph->image.w] = bmp->buffer[x+y*bmp->pitch];
371 printf("Loaded %u glyphs\n", font->n_glyphs);
375 font->kerning = NULL;
376 for(i=0; i<font->n_glyphs; ++i) for(j=0; j<font->n_glyphs; ++j)
380 FT_Get_Kerning(face, font->glyphs[i].index, font->glyphs[j].index, FT_KERNING_DEFAULT, &kerning);
382 /* FreeType documentation says that vertical kerning is practically
383 never used, so we ignore it. */
388 if(font->n_kerning>=size)
391 font->kerning = (Kerning *)realloc(font->kerning, size*sizeof(Kerning));
394 kern = &font->kerning[font->n_kerning++];
395 kern->left_code = font->glyphs[i].code;
396 kern->right_code = font->glyphs[j].code;
397 kern->distance = kerning.x/64;
402 printf("Loaded %d kerning pairs\n", font->n_kerning);
405 void render_grid(Font *font, unsigned cellw, unsigned cellh, unsigned cpl, int seq)
408 int top = 0, bot = 0;
409 unsigned first, last;
410 unsigned maxw = 0, maxh = 0;
412 /* Find extremes of the glyph images. */
413 for(i=0; i<font->n_glyphs; ++i)
417 y = font->glyphs[i].offset_y+font->glyphs[i].image.h;
420 if(font->glyphs[i].offset_y<bot)
421 bot = font->glyphs[i].offset_y;
422 if(font->glyphs[i].image.w>maxw)
423 maxw = font->glyphs[i].image.w;
424 if(font->glyphs[i].image.h>maxh)
425 maxh = font->glyphs[i].image.h;
430 /* Establish a large enough cell to hold all glyphs in the range. */
431 int square = (cellh==cellw);
445 printf("Max size: %u x %u\n", maxw, maxh);
446 printf("Y range: [%d %d]\n", bot, top);
447 printf("Cell size: %u x %u\n", cellw, cellh);
448 if(maxw>cellw || (unsigned)(top-bot)>cellh)
449 fprintf(stderr, "Warning: character size exceeds cell size\n");
454 /* Determine number of characters per line, trying to fit all the glyphs
455 in a square image. */
459 if(cpl>0 && font->n_glyphs/cpl*cellh<=cpl*cellw)
464 first = font->glyphs[0].code;
467 last = font->glyphs[font->n_glyphs-1].code;
469 font->image.w = round_to_pot(cpl*cellw);
470 font->image.h = round_to_pot((last-first+cpl)/cpl*cellh);
472 font->image.data = (char *)malloc(font->image.w*font->image.h);
473 memset(font->image.data, 255, font->image.w*font->image.h);
475 for(i=0; i<font->n_glyphs; ++i)
481 glyph = &font->glyphs[i];
486 ci = glyph->code-first;
491 if(cellw>glyph->image.w)
492 cx += (cellw-glyph->image.w)/2;
493 cy += top-glyph->offset_y-glyph->image.h;
498 for(y=0; y<glyph->image.h; ++y) for(x=0; x<glyph->image.w; ++x)
500 if(cx+x>=font->image.w || cy+y>=font->image.h)
502 font->image.data[cx+x+(cy+y)*font->image.w] = 255-glyph->image.data[x+y*glyph->image.w];
507 void render_packed(Font *font)
512 unsigned *used_pixels;
516 /* Compute the total area occupied by glyphs and padding. */
517 for(i=0; i<font->n_glyphs; ++i)
518 area += (font->glyphs[i].image.w+1)*(font->glyphs[i].image.h+1);
520 /* Find an image size that's no higher than wide, allowing for some
521 imperfections in the packing. */
522 for(font->image.w=1;; font->image.w<<=1)
524 font->image.h = (area*5/4)/font->image.w;
525 if(font->image.h<=font->image.w)
528 font->image.h = round_to_pot(font->image.h);
530 /* Allocate arrays for storing the image and keeping track of used pixels and
531 glyphs. Since glyphs are rectangular and the image is filled starting from
532 the top, it's enough to track the number of used pixels at the top of each
534 font->image.data = (char *)malloc(font->image.w*font->image.h);
535 memset(font->image.data, 255, font->image.w*font->image.h);
536 used_pixels = (unsigned *)malloc(font->image.w*sizeof(unsigned));
537 memset(used_pixels, 0, font->image.w*sizeof(unsigned));
538 used_glyphs = (char *)malloc(font->n_glyphs);
539 memset(used_glyphs, 0, font->n_glyphs);
541 for(cy=0; cy<font->image.h;)
546 unsigned best_score = 0;
547 unsigned target_h = 0;
549 /* Find the leftmost free pixel on this row. Also record the lowest extent of glyphs
550 to the left of the free position. */
551 for(; (cx<font->image.w && used_pixels[cx]>cy); ++cx)
552 if(used_pixels[cx]-cy-1>target_h)
553 target_h = used_pixels[cx]-cy-1;
555 if(cx>=font->image.w)
562 /* Count the free pixel at this position. */
563 for(w=0; (cx+w<font->image.w && used_pixels[cx+w]<=cy); ++w) ;
565 /* Find a suitable glyph to put here. */
566 for(i=0; i<font->n_glyphs; ++i)
570 g = &font->glyphs[i];
571 if(!used_glyphs[i] && g->image.w<=w)
575 /* Prefer glyphs that would reach exactly as low as the ones left
576 of here. This aims to create a straight edge at the bottom for
577 lining up further glyphs. */
578 score = g->image.h+1;
579 if(g->image.h==target_h)
598 used_glyphs[glyph-font->glyphs] = 1;
602 for(y=0; y<glyph->image.h; ++y) for(x=0; x<glyph->image.w; ++x)
604 if(cx+x>=font->image.w || cy+y>=font->image.h)
606 font->image.data[cx+x+(cy+y)*font->image.w] = 255-glyph->image.data[x+y*glyph->image.w];
608 for(x=0; x<glyph->image.w+2; ++x)
610 if(cx+x<1 || cx+x>font->image.w)
612 if(used_pixels[cx+x-1]<cy+glyph->image.h+1)
613 used_pixels[cx+x-1] = cy+glyph->image.h+1;
616 if(cy+glyph->image.h>used_h)
617 used_h = cy+glyph->image.h;
620 /* Trim the image to the actually used size, in case the original estimate
621 was too pessimistic. */
622 font->image.h = round_to_pot(used_h);
625 int save_defs(const char *fn, const Font *font)
630 out = fopen(fn, "w");
633 fprintf(stderr, "Couldn't open %s\n",fn);
637 fprintf(out, "# Image/font info:\n");
638 fprintf(out, "# width height size ascent descent\n");
639 fprintf(out, "font %d %d %d %d %d\n", font->image.w, font->image.h, font->size, font->ascent, font->descent);
640 fprintf(out, "\n# Glyph info:\n");
641 fprintf(out, "# code x y width height offset_x offset_y advance\n");
642 for(i=0; i<font->n_glyphs; ++i)
644 const Glyph *g = &font->glyphs[i];
645 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);
647 fprintf(out, "\n# Kerning info:\n");
648 fprintf(out, "# left right distance\n");
649 for(i=0; i<font->n_kerning; ++i)
651 const Kerning *k = &font->kerning[i];
652 fprintf(out, "kern %u %u %d\n", k->left_code, k->right_code, k->distance);
660 int save_png(const char *fn, const Image *image, char alpha)
674 out = fopen(fn, "wb");
677 fprintf(stderr, "Couldn't open %s\n",fn);
682 pngs = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
685 fprintf(stderr, "Error writing PNG file\n");
688 pngi = png_create_info_struct(pngs);
691 png_destroy_write_struct(&pngs, NULL);
692 fprintf(stderr, "Error writing PNG file\n");
696 png_init_io(pngs, out);
697 rows = (png_byte **)malloc(image->h*sizeof(png_byte *));
700 data2 = (png_byte *)malloc(image->w*image->h*2);
701 for(i=0; i<image->w*image->h; ++i)
704 data2[i*2+1] = 255-image->data[i];
706 for(i=0; i<image->h; ++i)
707 rows[i] = (png_byte *)(data2+i*image->w*2);
708 color = PNG_COLOR_TYPE_GRAY_ALPHA;
712 for(i=0; i<image->h; ++i)
713 rows[i] = (png_byte *)(image->data+i*image->w);
714 color = PNG_COLOR_TYPE_GRAY;
716 png_set_IHDR(pngs, pngi, image->w, image->h, 8, color, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
717 png_set_rows(pngs, pngi, rows);
718 png_write_png(pngs, pngi, PNG_TRANSFORM_IDENTITY, NULL);
719 png_destroy_write_struct(&pngs, &pngi);
725 printf("Saved %dx%d PNG image to %s\n", image->w, image->h, fn);