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);
196 if(!strcmp(out_fn, "-"))
207 err = FT_Init_FreeType(&freetype);
210 fprintf(stderr, "Couldn't initialize FreeType library\n");
214 err = FT_New_Face(freetype, fn, 0, &face);
217 fprintf(stderr, "Couldn't load font file\n");
218 if(err==FT_Err_Unknown_File_Format)
219 fprintf(stderr, "Unknown file format\n");
225 const char *name = FT_Get_Postscript_Name(face);
226 printf("Font name: %s\n", name);
227 printf("Glyphs: %ld\n", face->num_glyphs);
230 err = FT_Set_Pixel_Sizes(face, 0, size);
233 fprintf(stderr, "Couldn't set size\n");
238 init_font(&font, face, begin, end, autohinter);
240 render_packed(&font);
242 render_grid(&font, cellw, cellh, cpl, seq);
245 for(i=0; (unsigned)i<font.image.w*font.image.h; ++i)
246 font.image.data[i] = 255-font.image.data[i];
248 save_png(out_fn, &font.image, alpha);
250 save_defs(def_fn, &font);
252 for(i=0; (unsigned)i<font.n_glyphs; ++i)
253 free(font.glyphs[i].image.data);
255 free(font.image.data);
258 FT_Done_FreeType(freetype);
263 unsigned round_to_pot(unsigned n)
277 printf("ttf2png - True Type Font to PNG converter\n"
278 "Copyright (c) 2004-2008 Mikko Rasa, Mikkosoft Productions\n"
279 "Distributed under the GNU General Public License\n\n");
281 printf("Usage: ttf2png [options] <TTF file>\n\n");
283 printf("Accepted options (default values in [brackets])\n"
284 " -r Range of characters to convert [0,255]\n"
285 " -s Font size to use, in pixels [10]\n"
286 " -l Number of characters to put in one line [auto]\n"
287 " -c Character cell size, in pixels [auto]\n"
288 " -o Output file name (or - for stdout) [font.png]\n");
289 printf(" -a Force autohinter\n"
290 " -t Render glyphs to alpha channel\n"
291 " -i Invert colors of the glyphs\n"
292 " -v Increase the level of verbosity\n"
293 " -e Use cells in sequence, without gaps\n"
294 " -p Pack the glyphs tightly instead of in a grid\n"
295 " -d File name for writing glyph definitions\n"
296 " -h Print this message\n");
299 void init_font(Font *font, FT_Face face, unsigned first, unsigned last, int autohinter)
304 font->ascent = (face->size->metrics.ascender+63)>>6;
305 font->descent = (face->size->metrics.descender+63)>>6;
309 printf("Ascent: %d\n", font->ascent);
310 printf("Descent: %d\n", font->descent);
315 for(i=first; i<=last; ++i)
318 FT_Bitmap *bmp = &face->glyph->bitmap;
323 n = FT_Get_Char_Index(face, i);
328 flags |= FT_LOAD_FORCE_AUTOHINT;
329 FT_Load_Glyph(face, n, flags);
330 FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
333 printf(" Char %u: glyph %u, size %dx%d\n", i, n, bmp->width, bmp->rows);
335 if(bmp->pixel_mode!=FT_PIXEL_MODE_GRAY)
337 fprintf(stderr, "Warning: Glyph %u skipped, not grayscale\n", n);
341 if(font->n_glyphs>=size)
344 font->glyphs = (Glyph *)realloc(font->glyphs, size*sizeof(Glyph));
347 glyph = &font->glyphs[font->n_glyphs++];
350 glyph->image.w = bmp->width;
351 glyph->image.h = bmp->rows;
352 glyph->image.data = (char *)malloc(bmp->width*bmp->rows);
353 glyph->offset_x = face->glyph->bitmap_left;
354 glyph->offset_y = face->glyph->bitmap_top-bmp->rows;
355 glyph->advance = (int)(face->glyph->advance.x+32)/64;
357 /* Copy the glyph image since FreeType uses a global buffer, which would
358 be overwritten by the next glyph. Negative pitch means the scanlines
359 start from the bottom */
362 for(y=0; y<bmp->rows; ++y) for(x=0; x<bmp->width; ++x)
363 glyph->image.data[x+(glyph->image.h-1-y)*glyph->image.w] = bmp->buffer[x-y*bmp->pitch];
367 for(y=0; y<bmp->rows; ++y) for(x=0; x<bmp->width; ++x)
368 glyph->image.data[x+y*glyph->image.w] = bmp->buffer[x+y*bmp->pitch];
373 printf("Loaded %u glyphs\n", font->n_glyphs);
377 font->kerning = NULL;
378 for(i=0; i<font->n_glyphs; ++i) for(j=0; j<font->n_glyphs; ++j)
382 FT_Get_Kerning(face, font->glyphs[i].index, font->glyphs[j].index, FT_KERNING_DEFAULT, &kerning);
384 /* FreeType documentation says that vertical kerning is practically
385 never used, so we ignore it. */
390 if(font->n_kerning>=size)
393 font->kerning = (Kerning *)realloc(font->kerning, size*sizeof(Kerning));
396 kern = &font->kerning[font->n_kerning++];
397 kern->left_code = font->glyphs[i].code;
398 kern->right_code = font->glyphs[j].code;
399 kern->distance = kerning.x/64;
404 printf("Loaded %d kerning pairs\n", font->n_kerning);
407 void render_grid(Font *font, unsigned cellw, unsigned cellh, unsigned cpl, int seq)
410 int top = 0, bot = 0;
411 unsigned first, last;
412 unsigned maxw = 0, maxh = 0;
414 /* Find extremes of the glyph images. */
415 for(i=0; i<font->n_glyphs; ++i)
419 y = font->glyphs[i].offset_y+font->glyphs[i].image.h;
422 if(font->glyphs[i].offset_y<bot)
423 bot = font->glyphs[i].offset_y;
424 if(font->glyphs[i].image.w>maxw)
425 maxw = font->glyphs[i].image.w;
426 if(font->glyphs[i].image.h>maxh)
427 maxh = font->glyphs[i].image.h;
432 /* Establish a large enough cell to hold all glyphs in the range. */
433 int square = (cellh==cellw);
447 printf("Max size: %u x %u\n", maxw, maxh);
448 printf("Y range: [%d %d]\n", bot, top);
449 printf("Cell size: %u x %u\n", cellw, cellh);
450 if(maxw>cellw || (unsigned)(top-bot)>cellh)
451 fprintf(stderr, "Warning: character size exceeds cell size\n");
456 /* Determine number of characters per line, trying to fit all the glyphs
457 in a square image. */
461 if(cpl>0 && font->n_glyphs/cpl*cellh<=cpl*cellw)
466 first = font->glyphs[0].code;
469 last = font->glyphs[font->n_glyphs-1].code;
471 font->image.w = round_to_pot(cpl*cellw);
472 font->image.h = round_to_pot((last-first+cpl)/cpl*cellh);
474 font->image.data = (char *)malloc(font->image.w*font->image.h);
475 memset(font->image.data, 255, font->image.w*font->image.h);
477 for(i=0; i<font->n_glyphs; ++i)
483 glyph = &font->glyphs[i];
488 ci = glyph->code-first;
493 if(cellw>glyph->image.w)
494 cx += (cellw-glyph->image.w)/2;
495 cy += top-glyph->offset_y-glyph->image.h;
500 for(y=0; y<glyph->image.h; ++y) for(x=0; x<glyph->image.w; ++x)
502 if(cx+x>=font->image.w || cy+y>=font->image.h)
504 font->image.data[cx+x+(cy+y)*font->image.w] = 255-glyph->image.data[x+y*glyph->image.w];
509 void render_packed(Font *font)
514 unsigned *used_pixels;
518 /* Compute the total area occupied by glyphs and padding. */
519 for(i=0; i<font->n_glyphs; ++i)
520 area += (font->glyphs[i].image.w+1)*(font->glyphs[i].image.h+1);
522 /* Find an image size that's no higher than wide, allowing for some
523 imperfections in the packing. */
524 for(font->image.w=1;; font->image.w<<=1)
526 font->image.h = (area*5/4)/font->image.w;
527 if(font->image.h<=font->image.w)
530 font->image.h = round_to_pot(font->image.h);
532 /* Allocate arrays for storing the image and keeping track of used pixels and
533 glyphs. Since glyphs are rectangular and the image is filled starting from
534 the top, it's enough to track the number of used pixels at the top of each
536 font->image.data = (char *)malloc(font->image.w*font->image.h);
537 memset(font->image.data, 255, font->image.w*font->image.h);
538 used_pixels = (unsigned *)malloc(font->image.w*sizeof(unsigned));
539 memset(used_pixels, 0, font->image.w*sizeof(unsigned));
540 used_glyphs = (char *)malloc(font->n_glyphs);
541 memset(used_glyphs, 0, font->n_glyphs);
543 for(cy=0; cy<font->image.h;)
548 unsigned best_score = 0;
549 unsigned target_h = 0;
551 /* Find the leftmost free pixel on this row. Also record the lowest extent of glyphs
552 to the left of the free position. */
553 for(; (cx<font->image.w && used_pixels[cx]>cy); ++cx)
554 if(used_pixels[cx]-cy-1>target_h)
555 target_h = used_pixels[cx]-cy-1;
557 if(cx>=font->image.w)
564 /* Count the free pixel at this position. */
565 for(w=0; (cx+w<font->image.w && used_pixels[cx+w]<=cy); ++w) ;
567 /* Find a suitable glyph to put here. */
568 for(i=0; i<font->n_glyphs; ++i)
572 g = &font->glyphs[i];
573 if(!used_glyphs[i] && g->image.w<=w)
577 /* Prefer glyphs that would reach exactly as low as the ones left
578 of here. This aims to create a straight edge at the bottom for
579 lining up further glyphs. */
580 score = g->image.h+1;
581 if(g->image.h==target_h)
600 used_glyphs[glyph-font->glyphs] = 1;
604 for(y=0; y<glyph->image.h; ++y) for(x=0; x<glyph->image.w; ++x)
606 if(cx+x>=font->image.w || cy+y>=font->image.h)
608 font->image.data[cx+x+(cy+y)*font->image.w] = 255-glyph->image.data[x+y*glyph->image.w];
610 for(x=0; x<glyph->image.w+2; ++x)
612 if(cx+x<1 || cx+x>font->image.w)
614 if(used_pixels[cx+x-1]<cy+glyph->image.h+1)
615 used_pixels[cx+x-1] = cy+glyph->image.h+1;
618 if(cy+glyph->image.h>used_h)
619 used_h = cy+glyph->image.h;
622 /* Trim the image to the actually used size, in case the original estimate
623 was too pessimistic. */
624 font->image.h = round_to_pot(used_h);
627 int save_defs(const char *fn, const Font *font)
632 out = fopen(fn, "w");
635 fprintf(stderr, "Couldn't open %s\n",fn);
639 fprintf(out, "# Image/font info:\n");
640 fprintf(out, "# width height size ascent descent\n");
641 fprintf(out, "font %d %d %d %d %d\n", font->image.w, font->image.h, font->size, font->ascent, font->descent);
642 fprintf(out, "\n# Glyph info:\n");
643 fprintf(out, "# code x y width height offset_x offset_y advance\n");
644 for(i=0; i<font->n_glyphs; ++i)
646 const Glyph *g = &font->glyphs[i];
647 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);
649 fprintf(out, "\n# Kerning info:\n");
650 fprintf(out, "# left right distance\n");
651 for(i=0; i<font->n_kerning; ++i)
653 const Kerning *k = &font->kerning[i];
654 fprintf(out, "kern %u %u %d\n", k->left_code, k->right_code, k->distance);
662 int save_png(const char *fn, const Image *image, char alpha)
676 out = fopen(fn, "wb");
679 fprintf(stderr, "Couldn't open %s\n",fn);
684 pngs = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
687 fprintf(stderr, "Error writing PNG file\n");
690 pngi = png_create_info_struct(pngs);
693 png_destroy_write_struct(&pngs, NULL);
694 fprintf(stderr, "Error writing PNG file\n");
698 png_init_io(pngs, out);
699 rows = (png_byte **)malloc(image->h*sizeof(png_byte *));
702 data2 = (png_byte *)malloc(image->w*image->h*2);
703 for(i=0; i<image->w*image->h; ++i)
706 data2[i*2+1] = 255-image->data[i];
708 for(i=0; i<image->h; ++i)
709 rows[i] = (png_byte *)(data2+i*image->w*2);
710 color = PNG_COLOR_TYPE_GRAY_ALPHA;
714 for(i=0; i<image->h; ++i)
715 rows[i] = (png_byte *)(image->data+i*image->w);
716 color = PNG_COLOR_TYPE_GRAY;
718 png_set_IHDR(pngs, pngi, image->w, image->h, 8, color, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
719 png_set_rows(pngs, pngi, rows);
720 png_write_png(pngs, pngi, PNG_TRANSFORM_IDENTITY, NULL);
721 png_destroy_write_struct(&pngs, &pngi);
727 printf("Saved %dx%d PNG image to %s\n", image->w, image->h, fn);