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
54 unsigned round_to_pot(unsigned);
56 void init_font(Font *, FT_Face, unsigned, unsigned, int);
57 void render_grid(Font *, unsigned, unsigned, unsigned, int);
58 void render_packed(Font *);
59 int save_defs(const char *, const Font *);
60 int save_png(const char *, const Image *, char);
64 int main(int argc, char **argv)
85 char *out_fn = "font.png";
96 while((i = getopt(argc, argv, "r:s:l:c:o:atvh?ed:pi")) != -1)
103 if(!strcmp(optarg, "all"))
110 if(!isdigit(optarg[0]))
114 temp = strtol(optarg, &ptr, 0);
115 if(ptr[0]!=',' || !isdigit(ptr[1]))
120 printf("Not a valid range: %s\n", optarg);
126 end = strtol(ptr+1, NULL, 0);
131 size = strtol(optarg, NULL, 0);
134 cpl = strtol(optarg, NULL, 0);
137 if(!strcmp(optarg, "auto"))
142 else if(!strcmp(optarg, "autorect"))
149 cellw = strtol(optarg, &ptr, 0);
150 if(ptr[0]=='x' && isdigit(ptr[1]))
151 cellh = strtol(ptr+1, NULL, 0);
184 if(!strcmp(out_fn, "-"))
195 err = FT_Init_FreeType(&freetype);
198 fprintf(stderr, "Couldn't initialize FreeType library\n");
202 err = FT_New_Face(freetype, fn, 0, &face);
205 fprintf(stderr, "Couldn't load font file\n");
206 if(err==FT_Err_Unknown_File_Format)
207 fprintf(stderr, "Unknown file format\n");
213 const char *name = FT_Get_Postscript_Name(face);
214 printf("Font name: %s\n", name);
215 printf("Glyphs: %ld\n", face->num_glyphs);
218 err = FT_Set_Pixel_Sizes(face, 0, size);
221 fprintf(stderr, "Couldn't set size\n");
226 init_font(&font, face, begin, end, autohinter);
228 render_packed(&font);
230 render_grid(&font, cellw, cellh, cpl, seq);
233 for(i=0; i<font.image.w*font.image.h; ++i)
234 font.image.data[i] = 255-font.image.data[i];
236 save_png(out_fn, &font.image, alpha);
238 save_defs(def_fn, &font);
240 for(i=0; i<font.n_glyphs; ++i)
241 free(font.glyphs[i].image.data);
243 free(font.image.data);
246 FT_Done_FreeType(freetype);
251 unsigned round_to_pot(unsigned n)
265 printf("ttf2png - True Type Font to PNG converter\n"
266 "Copyright (c) 2004-2008 Mikko Rasa, Mikkosoft Productions\n"
267 "Distributed under the GNU General Public License\n\n"
268 "Usage: ttf2png [options] <TTF file>\n\n"
269 "Accepted options (default values in [brackets])\n"
270 " -r Range of characters to convert [0,255]\n"
271 " -s Font size to use, in pixels [10]\n"
272 " -l Number of characters to put in one line [auto]\n"
273 " -c Character cell size, in pixels [auto]\n"
274 " -o Output file name (or - for stdout) [font.png]\n"
275 " -a Force autohinter\n"
276 " -t Render glyphs to alpha channel\n"
277 " -i Invert colors of the glyphs\n"
278 " -v Increase the level of verbosity\n"
279 " -e Use cells in sequence, without gaps\n"
280 " -p Pack the glyphs tightly instead of in a grid\n"
281 " -d File name for writing glyph definitions\n"
282 " -h Print this message\n");
285 void init_font(Font *font, FT_Face face, unsigned first, unsigned last, int autohinter)
290 font->ascent = (face->size->metrics.ascender+63)>>6;
291 font->descent = (face->size->metrics.descender+63)>>6;
295 printf("Ascent: %d\n", font->ascent);
296 printf("Descent: %d\n", font->descent);
301 for(i=first; i<=last; ++i)
304 FT_Bitmap *bmp = &face->glyph->bitmap;
309 n = FT_Get_Char_Index(face, i);
314 flags |= FT_LOAD_FORCE_AUTOHINT;
315 FT_Load_Glyph(face, n, flags);
316 FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
319 printf(" Char %u: glyph %u, size %dx%d\n", i, n, bmp->width, bmp->rows);
321 if(bmp->pixel_mode!=FT_PIXEL_MODE_GRAY)
323 fprintf(stderr, "Warning: Glyph %u skipped, not grayscale\n", n);
327 if(font->n_glyphs>=size)
330 font->glyphs = (Glyph *)realloc(font->glyphs, size*sizeof(Glyph));
333 glyph = &font->glyphs[font->n_glyphs++];
335 glyph->image.w = bmp->width;
336 glyph->image.h = bmp->rows;
337 glyph->image.data = (char *)malloc(bmp->width*bmp->rows);
338 glyph->offset_x = face->glyph->bitmap_left;
339 glyph->offset_y = face->glyph->bitmap_top-bmp->rows;
340 glyph->advance = (int)(face->glyph->advance.x+32)/64;
342 /* Copy the glyph image since FreeType uses a global buffer, which would
343 be overwritten by the next glyph. Negative pitch means the scanlines
344 start from the bottom */
347 for(y=0; y<bmp->rows; ++y) for(x=0; x<bmp->width; ++x)
348 glyph->image.data[x+(glyph->image.h-1-y)*glyph->image.w] = bmp->buffer[x-y*bmp->pitch];
352 for(y=0; y<bmp->rows; ++y) for(x=0; x<bmp->width; ++x)
353 glyph->image.data[x+y*glyph->image.w] = bmp->buffer[x+y*bmp->pitch];
358 printf("Loaded %u glyphs\n", font->n_glyphs);
361 void render_grid(Font *font, unsigned cellw, unsigned cellh, unsigned cpl, int seq)
364 int top = 0, bot = 0;
365 unsigned first, last;
366 unsigned maxw = 0, maxh = 0;
368 /* Find extremes of the glyph images. */
369 for(i=0; i<font->n_glyphs; ++i)
373 y = font->glyphs[i].offset_y+font->glyphs[i].image.h;
376 if(font->glyphs[i].offset_y<bot)
377 bot = font->glyphs[i].offset_y;
378 if(font->glyphs[i].image.w>maxw)
379 maxw = font->glyphs[i].image.w;
380 if(font->glyphs[i].image.h>maxh)
381 maxh = font->glyphs[i].image.h;
386 /* Establish a large enough cell to hold all glyphs in the range. */
387 int square = (cellh==cellw);
401 printf("Max size: %u x %u\n", maxw, maxh);
402 printf("Y range: [%d %d]\n", bot, top);
403 printf("Cell size: %u x %u\n", cellw, cellh);
404 if(maxw>cellw || top-bot>cellh)
405 fprintf(stderr, "Warning: character size exceeds cell size\n");
410 /* Determine number of characters per line, trying to fit all the glyphs
411 in a square image. */
415 if(cpl>0 && font->n_glyphs/cpl*cellh<=cpl*cellw)
420 first = font->glyphs[0].code;
423 last = font->glyphs[font->n_glyphs-1].code;
425 font->image.w = round_to_pot(cpl*cellw);
426 font->image.h = round_to_pot((last-first+cpl)/cpl*cellh);
428 font->image.data = (char *)malloc(font->image.w*font->image.h);
429 memset(font->image.data, 255, font->image.w*font->image.h);
431 for(i=0; i<font->n_glyphs; ++i)
437 glyph = &font->glyphs[i];
442 ci = glyph->code-first;
447 if(cellw>glyph->image.w)
448 cx += (cellw-glyph->image.w)/2;
449 cy += top-glyph->offset_y-glyph->image.h;
454 for(y=0; y<glyph->image.h; ++y) for(x=0; x<glyph->image.w; ++x)
456 if(cx+x<0 || cx+x>=font->image.w || cy+y<0 || cy+y>=font->image.h)
458 font->image.data[cx+x+(cy+y)*font->image.w] = 255-glyph->image.data[x+y*glyph->image.w];
463 void render_packed(Font *font)
468 unsigned *used_pixels;
472 /* Compute the total area occupied by glyphs and padding. */
473 for(i=0; i<font->n_glyphs; ++i)
474 area += (font->glyphs[i].image.w+1)*(font->glyphs[i].image.h+1);
476 /* Find an image size that's no higher than wide, allowing for some
477 imperfections in the packing. */
478 for(font->image.w=1;; font->image.w<<=1)
480 font->image.h = (area*5/4)/font->image.w;
481 if(font->image.h<=font->image.w)
484 font->image.h = round_to_pot(font->image.h);
486 /* Allocate arrays for storing the image and keeping track of used pixels and
487 glyphs. Since glyphs are rectangular and the image is filled starting from
488 the top, it's enough to track the number of used pixels at the top of each
490 font->image.data = (char *)malloc(font->image.w*font->image.h);
491 memset(font->image.data, 255, font->image.w*font->image.h);
492 used_pixels = (unsigned *)malloc(font->image.w*sizeof(unsigned));
493 memset(used_pixels, 0, font->image.w*sizeof(unsigned));
494 used_glyphs = (char *)malloc(font->n_glyphs);
495 memset(used_glyphs, 0, font->n_glyphs);
497 for(cy=0; cy<font->image.h;)
502 unsigned best_score = 0;
503 unsigned target_h = 0;
505 /* Find the leftmost free pixel on this row. Also record the lowest extent of glyphs
506 to the left of the free position. */
507 for(; (cx<font->image.w && used_pixels[cx]>cy); ++cx)
508 if(used_pixels[cx]-cy-1>target_h)
509 target_h = used_pixels[cx]-cy-1;
511 if(cx>=font->image.w)
518 /* Count the free pixel at this position. */
519 for(w=0; (cx+w<font->image.w && used_pixels[cx+w]<=cy); ++w) ;
521 /* Find a suitable glyph to put here. */
522 for(i=0; i<font->n_glyphs; ++i)
526 g = &font->glyphs[i];
527 if(!used_glyphs[i] && g->image.w<=w)
531 /* Prefer glyphs that would reach exactly as low as the ones left
532 of here. This aims to create a straight edge at the bottom for
533 lining up further glyphs. */
534 score = g->image.h+1;
535 if(g->image.h==target_h)
554 used_glyphs[glyph-font->glyphs] = 1;
558 for(y=0; y<glyph->image.h; ++y) for(x=0; x<glyph->image.w; ++x)
560 if(cx+x<0 || cx+x>=font->image.w || cy+y<0 || cy+y>=font->image.h)
562 font->image.data[cx+x+(cy+y)*font->image.w] = 255-glyph->image.data[x+y*glyph->image.w];
564 for(x=0; x<glyph->image.w+2; ++x)
566 if(cx+x<1 || cx+x>font->image.w)
568 if(used_pixels[cx+x-1]<cy+glyph->image.h+1)
569 used_pixels[cx+x-1] = cy+glyph->image.h+1;
572 if(cy+glyph->image.h>used_h)
573 used_h = cy+glyph->image.h;
576 /* Trim the image to the actually used size, in case the original estimate
577 was too pessimistic. */
578 font->image.h = round_to_pot(used_h);
581 int save_defs(const char *fn, const Font *font)
586 out = fopen(fn, "w");
589 fprintf(stderr, "Couldn't open %s\n",fn);
593 fprintf(out, "# Image/font info:\n");
594 fprintf(out, "# width height size ascent descent\n");
595 fprintf(out, "%d %d %d %d %d\n", font->image.w, font->image.h, font->size, font->ascent, font->descent);
596 fprintf(out, "\n# Glyph info:\n");
597 fprintf(out, "# code x y width height offset_x offset_y advance\n");
598 for(i=0; i<font->n_glyphs; ++i)
600 const Glyph *g = &font->glyphs[i];
601 fprintf(out, "%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);
609 int save_png(const char *fn, const Image *image, char alpha)
614 png_byte *rows[image->h];
623 out = fopen(fn, "wb");
626 fprintf(stderr, "Couldn't open %s\n",fn);
631 pngs = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
634 fprintf(stderr, "Error writing PNG file\n");
637 pngi = png_create_info_struct(pngs);
640 png_destroy_write_struct(&pngs, NULL);
641 fprintf(stderr, "Error writing PNG file\n");
645 png_init_io(pngs, out);
648 data2 = (png_byte *)malloc(image->w*image->h*2);
649 for(i=0; i<image->w*image->h; ++i)
652 data2[i*2+1] = 255-image->data[i];
654 for(i=0; i<image->h; ++i)
655 rows[i] = (png_byte *)(data2+i*image->w*2);
656 color = PNG_COLOR_TYPE_GRAY_ALPHA;
660 for(i=0; i<image->h; ++i)
661 rows[i] = (png_byte *)(image->data+i*image->w);
662 color = PNG_COLOR_TYPE_GRAY;
664 png_set_IHDR(pngs, pngi, image->w, image->h, 8, color, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
665 png_set_rows(pngs, pngi, rows);
666 png_write_png(pngs, pngi, PNG_TRANSFORM_IDENTITY, NULL);
667 png_destroy_write_struct(&pngs, &pngi);
672 printf("Saved %dx%d PNG image to %s\n", image->w, image->h, fn);