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; 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; 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"
278 "Usage: ttf2png [options] <TTF file>\n\n"
279 "Accepted options (default values in [brackets])\n"
280 " -r Range of characters to convert [0,255]\n"
281 " -s Font size to use, in pixels [10]\n"
282 " -l Number of characters to put in one line [auto]\n"
283 " -c Character cell size, in pixels [auto]\n"
284 " -o Output file name (or - for stdout) [font.png]\n"
285 " -a Force autohinter\n"
286 " -t Render glyphs to alpha channel\n"
287 " -i Invert colors of the glyphs\n"
288 " -v Increase the level of verbosity\n"
289 " -e Use cells in sequence, without gaps\n"
290 " -p Pack the glyphs tightly instead of in a grid\n"
291 " -d File name for writing glyph definitions\n"
292 " -h Print this message\n");
295 void init_font(Font *font, FT_Face face, unsigned first, unsigned last, int autohinter)
300 font->ascent = (face->size->metrics.ascender+63)>>6;
301 font->descent = (face->size->metrics.descender+63)>>6;
305 printf("Ascent: %d\n", font->ascent);
306 printf("Descent: %d\n", font->descent);
311 for(i=first; i<=last; ++i)
314 FT_Bitmap *bmp = &face->glyph->bitmap;
319 n = FT_Get_Char_Index(face, i);
324 flags |= FT_LOAD_FORCE_AUTOHINT;
325 FT_Load_Glyph(face, n, flags);
326 FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
329 printf(" Char %u: glyph %u, size %dx%d\n", i, n, bmp->width, bmp->rows);
331 if(bmp->pixel_mode!=FT_PIXEL_MODE_GRAY)
333 fprintf(stderr, "Warning: Glyph %u skipped, not grayscale\n", n);
337 if(font->n_glyphs>=size)
340 font->glyphs = (Glyph *)realloc(font->glyphs, size*sizeof(Glyph));
343 glyph = &font->glyphs[font->n_glyphs++];
346 glyph->image.w = bmp->width;
347 glyph->image.h = bmp->rows;
348 glyph->image.data = (char *)malloc(bmp->width*bmp->rows);
349 glyph->offset_x = face->glyph->bitmap_left;
350 glyph->offset_y = face->glyph->bitmap_top-bmp->rows;
351 glyph->advance = (int)(face->glyph->advance.x+32)/64;
353 /* Copy the glyph image since FreeType uses a global buffer, which would
354 be overwritten by the next glyph. Negative pitch means the scanlines
355 start from the bottom */
358 for(y=0; y<bmp->rows; ++y) for(x=0; x<bmp->width; ++x)
359 glyph->image.data[x+(glyph->image.h-1-y)*glyph->image.w] = bmp->buffer[x-y*bmp->pitch];
363 for(y=0; y<bmp->rows; ++y) for(x=0; x<bmp->width; ++x)
364 glyph->image.data[x+y*glyph->image.w] = bmp->buffer[x+y*bmp->pitch];
369 printf("Loaded %u glyphs\n", font->n_glyphs);
373 font->kerning = NULL;
374 for(i=0; i<font->n_glyphs; ++i) for(j=0; j<font->n_glyphs; ++j)
378 FT_Get_Kerning(face, font->glyphs[i].index, font->glyphs[j].index, FT_KERNING_DEFAULT, &kerning);
380 /* FreeType documentation says that vertical kerning is practically
381 never used, so we ignore it. */
386 if(font->n_kerning>=size)
389 font->kerning = (Kerning *)realloc(font->kerning, size*sizeof(Kerning));
392 kern = &font->kerning[font->n_kerning++];
393 kern->left_code = font->glyphs[i].code;
394 kern->right_code = font->glyphs[j].code;
395 kern->distance = kerning.x/64;
400 printf("Loaded %d kerning pairs\n", font->n_kerning);
403 void render_grid(Font *font, unsigned cellw, unsigned cellh, unsigned cpl, int seq)
406 int top = 0, bot = 0;
407 unsigned first, last;
408 unsigned maxw = 0, maxh = 0;
410 /* Find extremes of the glyph images. */
411 for(i=0; i<font->n_glyphs; ++i)
415 y = font->glyphs[i].offset_y+font->glyphs[i].image.h;
418 if(font->glyphs[i].offset_y<bot)
419 bot = font->glyphs[i].offset_y;
420 if(font->glyphs[i].image.w>maxw)
421 maxw = font->glyphs[i].image.w;
422 if(font->glyphs[i].image.h>maxh)
423 maxh = font->glyphs[i].image.h;
428 /* Establish a large enough cell to hold all glyphs in the range. */
429 int square = (cellh==cellw);
443 printf("Max size: %u x %u\n", maxw, maxh);
444 printf("Y range: [%d %d]\n", bot, top);
445 printf("Cell size: %u x %u\n", cellw, cellh);
446 if(maxw>cellw || top-bot>cellh)
447 fprintf(stderr, "Warning: character size exceeds cell size\n");
452 /* Determine number of characters per line, trying to fit all the glyphs
453 in a square image. */
457 if(cpl>0 && font->n_glyphs/cpl*cellh<=cpl*cellw)
462 first = font->glyphs[0].code;
465 last = font->glyphs[font->n_glyphs-1].code;
467 font->image.w = round_to_pot(cpl*cellw);
468 font->image.h = round_to_pot((last-first+cpl)/cpl*cellh);
470 font->image.data = (char *)malloc(font->image.w*font->image.h);
471 memset(font->image.data, 255, font->image.w*font->image.h);
473 for(i=0; i<font->n_glyphs; ++i)
479 glyph = &font->glyphs[i];
484 ci = glyph->code-first;
489 if(cellw>glyph->image.w)
490 cx += (cellw-glyph->image.w)/2;
491 cy += top-glyph->offset_y-glyph->image.h;
496 for(y=0; y<glyph->image.h; ++y) for(x=0; x<glyph->image.w; ++x)
498 if(cx+x<0 || cx+x>=font->image.w || cy+y<0 || cy+y>=font->image.h)
500 font->image.data[cx+x+(cy+y)*font->image.w] = 255-glyph->image.data[x+y*glyph->image.w];
505 void render_packed(Font *font)
510 unsigned *used_pixels;
514 /* Compute the total area occupied by glyphs and padding. */
515 for(i=0; i<font->n_glyphs; ++i)
516 area += (font->glyphs[i].image.w+1)*(font->glyphs[i].image.h+1);
518 /* Find an image size that's no higher than wide, allowing for some
519 imperfections in the packing. */
520 for(font->image.w=1;; font->image.w<<=1)
522 font->image.h = (area*5/4)/font->image.w;
523 if(font->image.h<=font->image.w)
526 font->image.h = round_to_pot(font->image.h);
528 /* Allocate arrays for storing the image and keeping track of used pixels and
529 glyphs. Since glyphs are rectangular and the image is filled starting from
530 the top, it's enough to track the number of used pixels at the top of each
532 font->image.data = (char *)malloc(font->image.w*font->image.h);
533 memset(font->image.data, 255, font->image.w*font->image.h);
534 used_pixels = (unsigned *)malloc(font->image.w*sizeof(unsigned));
535 memset(used_pixels, 0, font->image.w*sizeof(unsigned));
536 used_glyphs = (char *)malloc(font->n_glyphs);
537 memset(used_glyphs, 0, font->n_glyphs);
539 for(cy=0; cy<font->image.h;)
544 unsigned best_score = 0;
545 unsigned target_h = 0;
547 /* Find the leftmost free pixel on this row. Also record the lowest extent of glyphs
548 to the left of the free position. */
549 for(; (cx<font->image.w && used_pixels[cx]>cy); ++cx)
550 if(used_pixels[cx]-cy-1>target_h)
551 target_h = used_pixels[cx]-cy-1;
553 if(cx>=font->image.w)
560 /* Count the free pixel at this position. */
561 for(w=0; (cx+w<font->image.w && used_pixels[cx+w]<=cy); ++w) ;
563 /* Find a suitable glyph to put here. */
564 for(i=0; i<font->n_glyphs; ++i)
568 g = &font->glyphs[i];
569 if(!used_glyphs[i] && g->image.w<=w)
573 /* Prefer glyphs that would reach exactly as low as the ones left
574 of here. This aims to create a straight edge at the bottom for
575 lining up further glyphs. */
576 score = g->image.h+1;
577 if(g->image.h==target_h)
596 used_glyphs[glyph-font->glyphs] = 1;
600 for(y=0; y<glyph->image.h; ++y) for(x=0; x<glyph->image.w; ++x)
602 if(cx+x<0 || cx+x>=font->image.w || cy+y<0 || cy+y>=font->image.h)
604 font->image.data[cx+x+(cy+y)*font->image.w] = 255-glyph->image.data[x+y*glyph->image.w];
606 for(x=0; x<glyph->image.w+2; ++x)
608 if(cx+x<1 || cx+x>font->image.w)
610 if(used_pixels[cx+x-1]<cy+glyph->image.h+1)
611 used_pixels[cx+x-1] = cy+glyph->image.h+1;
614 if(cy+glyph->image.h>used_h)
615 used_h = cy+glyph->image.h;
618 /* Trim the image to the actually used size, in case the original estimate
619 was too pessimistic. */
620 font->image.h = round_to_pot(used_h);
623 int save_defs(const char *fn, const Font *font)
628 out = fopen(fn, "w");
631 fprintf(stderr, "Couldn't open %s\n",fn);
635 fprintf(out, "# Image/font info:\n");
636 fprintf(out, "# width height size ascent descent\n");
637 fprintf(out, "font %d %d %d %d %d\n", font->image.w, font->image.h, font->size, font->ascent, font->descent);
638 fprintf(out, "\n# Glyph info:\n");
639 fprintf(out, "# code x y width height offset_x offset_y advance\n");
640 for(i=0; i<font->n_glyphs; ++i)
642 const Glyph *g = &font->glyphs[i];
643 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);
645 fprintf(out, "\n# Kerning info:\n");
646 fprintf(out, "# left right distance\n");
647 for(i=0; i<font->n_kerning; ++i)
649 const Kerning *k = &font->kerning[i];
650 fprintf(out, "kern %u %u %d\n", k->left_code, k->right_code, k->distance);
658 int save_png(const char *fn, const Image *image, char alpha)
663 png_byte *rows[image->h];
672 out = fopen(fn, "wb");
675 fprintf(stderr, "Couldn't open %s\n",fn);
680 pngs = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
683 fprintf(stderr, "Error writing PNG file\n");
686 pngi = png_create_info_struct(pngs);
689 png_destroy_write_struct(&pngs, NULL);
690 fprintf(stderr, "Error writing PNG file\n");
694 png_init_io(pngs, out);
697 data2 = (png_byte *)malloc(image->w*image->h*2);
698 for(i=0; i<image->w*image->h; ++i)
701 data2[i*2+1] = 255-image->data[i];
703 for(i=0; i<image->h; ++i)
704 rows[i] = (png_byte *)(data2+i*image->w*2);
705 color = PNG_COLOR_TYPE_GRAY_ALPHA;
709 for(i=0; i<image->h; ++i)
710 rows[i] = (png_byte *)(image->data+i*image->w);
711 color = PNG_COLOR_TYPE_GRAY;
713 png_set_IHDR(pngs, pngi, image->w, image->h, 8, color, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
714 png_set_rows(pngs, pngi, rows);
715 png_write_png(pngs, pngi, PNG_TRANSFORM_IDENTITY, NULL);
716 png_destroy_write_struct(&pngs, &pngi);
721 printf("Saved %dx%d PNG image to %s\n", image->w, image->h, fn);