]> git.tdb.fi Git - ttf2png.git/blob - ttf2png.c
Include kerning information in definition file
[ttf2png.git] / ttf2png.c
1 /*
2 ttf2png - True Type Font to PNG converter
3 Copyright (c) 2004-2008 Mikko Rasa
4
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.
9
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.
14
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
18 */
19
20 #include <stdio.h>
21 #include <ctype.h>
22 #include <unistd.h>
23 #include <getopt.h>
24 #include <png.h>
25 #include <ft2build.h>
26 #include FT_FREETYPE_H
27
28 typedef struct sImage
29 {
30         unsigned w, h;
31         char     *data;
32 } Image;
33
34 typedef struct sGlyph
35 {
36         unsigned index;
37         unsigned code;
38         Image    image;
39         unsigned x, y;
40         int      offset_x;
41         int      offset_y;
42         int      advance;
43 } Glyph;
44
45 typedef struct sKerning
46 {
47         unsigned left_code;
48         unsigned right_code;
49         int distance;
50 } Kerning;
51
52 typedef struct sFont
53 {
54         unsigned size;
55         int      ascent;
56         int      descent;
57         unsigned n_glyphs;
58         Glyph    *glyphs;
59         unsigned n_kerning;
60         Kerning  *kerning;
61         Image    image;
62 } Font;
63
64 unsigned round_to_pot(unsigned);
65 void usage();
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);
71
72 char verbose = 0;
73
74 int main(int argc, char **argv)
75 {
76         char *fn;
77         int  begin = 0;
78         int  end = 255;
79         int  size = 10;
80         int  cpl = 0;
81         int  cellw = 0;
82         int  cellh = 0;
83         char autohinter = 0;
84         char seq = 0;
85         char alpha = 0;
86         char invert = 0;
87         char pack = 0;
88
89         FT_Library freetype;
90         FT_Face    face;
91
92         int  err;
93         int  i;
94
95         char *out_fn = "font.png";
96         char *def_fn = NULL;
97
98         Font font;
99
100         if(argc<2)
101         {
102                 usage();
103                 return 1;
104         }
105
106         while((i = getopt(argc, argv, "r:s:l:c:o:atvh?ed:pi")) != -1)
107         {
108                 char *ptr;
109                 int  temp;
110                 switch(i)
111                 {
112                 case 'r':
113                         if(!strcmp(optarg, "all"))
114                         {
115                                 begin = 0;
116                                 end = 0x110000;
117                         }
118                         else
119                         {
120                                 if(!isdigit(optarg[0]))
121                                         temp = -1;
122                                 else
123                                 {
124                                         temp = strtol(optarg, &ptr, 0);
125                                         if(ptr[0]!=',' || !isdigit(ptr[1]))
126                                                 temp = -1;
127                                 }
128                                 if(temp<0)
129                                 {
130                                         printf("Not a valid range: %s\n", optarg);
131                                         exit(1);
132                                 }
133                                 else
134                                 {
135                                         begin = temp;
136                                         end = strtol(ptr+1, NULL, 0);
137                                 }
138                         }
139                         break;
140                 case 's':
141                         size = strtol(optarg, NULL, 0);
142                         break;
143                 case 'l':
144                         cpl = strtol(optarg, NULL, 0);
145                         break;
146                 case 'c':
147                         if(!strcmp(optarg, "auto"))
148                         {
149                                 cellw = 0;
150                                 cellh = 0;
151                         }
152                         else if(!strcmp(optarg, "autorect"))
153                         {
154                                 cellw = 0;
155                                 cellh = 1;
156                         }
157                         else
158                         {
159                                 cellw = strtol(optarg, &ptr, 0);
160                                 if(ptr[0]=='x' && isdigit(ptr[1]))
161                                         cellh = strtol(ptr+1, NULL, 0);
162                         }
163                         break;
164                 case 'o':
165                         out_fn = optarg;
166                         break;
167                 case 'a':
168                         autohinter = 1;
169                         break;
170                 case 't':
171                         alpha = 1;
172                         break;
173                 case 'v':
174                         ++verbose;
175                         break;
176                 case 'h':
177                 case '?':
178                         usage();
179                         return 0;
180                 case 'e':
181                         seq = 1;
182                         break;
183                 case 'd':
184                         def_fn = optarg;
185                         break;
186                 case 'p':
187                         pack = 1;
188                         break;
189                 case 'i':
190                         invert = 1;
191                         break;
192                 }
193         }
194         if(!strcmp(out_fn, "-"))
195                 verbose = 0;
196
197         if(optind!=argc-1)
198         {
199                 usage();
200                 return 1;
201         }
202
203         fn = argv[optind];
204
205         err = FT_Init_FreeType(&freetype);
206         if(err)
207         {
208                 fprintf(stderr, "Couldn't initialize FreeType library\n");
209                 return 1;
210         }
211
212         err = FT_New_Face(freetype, fn, 0, &face);
213         if(err)
214         {
215                 fprintf(stderr, "Couldn't load font file\n");
216                 if(err==FT_Err_Unknown_File_Format)
217                         fprintf(stderr, "Unknown file format\n");
218                 return 1;
219         }
220
221         if(verbose)
222         {
223                 const char *name = FT_Get_Postscript_Name(face);
224                 printf("Font name: %s\n", name);
225                 printf("Glyphs:    %ld\n", face->num_glyphs);
226         }
227
228         err = FT_Set_Pixel_Sizes(face, 0, size);
229         if(err)
230         {
231                 fprintf(stderr, "Couldn't set size\n");
232                 return 1;
233         }
234
235         font.size = size;
236         init_font(&font, face, begin, end, autohinter);
237         if(pack)
238                 render_packed(&font);
239         else
240                 render_grid(&font, cellw, cellh, cpl, seq);
241         if(invert)
242         {
243                 for(i=0; i<font.image.w*font.image.h; ++i)
244                         font.image.data[i] = 255-font.image.data[i];
245         }
246         save_png(out_fn, &font.image, alpha);
247         if(def_fn)
248                 save_defs(def_fn, &font);
249
250         for(i=0; i<font.n_glyphs; ++i)
251                 free(font.glyphs[i].image.data);
252         free(font.glyphs);
253         free(font.image.data);
254
255         FT_Done_Face(face);
256         FT_Done_FreeType(freetype);
257
258         return 0;
259 }
260
261 unsigned round_to_pot(unsigned n)
262 {
263         n -= 1;
264         n |= n>>1;
265         n |= n>>2;
266         n |= n>>4;
267         n |= n>>8;
268         n |= n>>16;
269
270         return n+1;
271 }
272
273 void usage()
274 {
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");
293 }
294
295 void init_font(Font *font, FT_Face face, unsigned first, unsigned last, int autohinter)
296 {
297         unsigned i, j;
298         unsigned size = 0;
299
300         font->ascent = (face->size->metrics.ascender+63)>>6;
301         font->descent = (face->size->metrics.descender+63)>>6;
302
303         if(verbose>=1)
304         {
305                 printf("Ascent:    %d\n", font->ascent);
306                 printf("Descent:   %d\n", font->descent);
307         }
308
309         font->n_glyphs = 0;
310         font->glyphs = NULL;
311         for(i=first; i<=last; ++i)
312         {
313                 unsigned  n;
314                 FT_Bitmap *bmp = &face->glyph->bitmap;
315                 int       x, y;
316                 int       flags = 0;
317                 Glyph     *glyph;
318
319                 n = FT_Get_Char_Index(face, i);
320                 if(!n)
321                         continue;
322
323                 if(autohinter)
324                         flags |= FT_LOAD_FORCE_AUTOHINT;
325                 FT_Load_Glyph(face, n, flags);
326                 FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
327
328                 if(verbose>=2)
329                         printf("  Char %u: glyph %u, size %dx%d\n", i, n, bmp->width, bmp->rows);
330
331                 if(bmp->pixel_mode!=FT_PIXEL_MODE_GRAY)
332                 {
333                         fprintf(stderr, "Warning: Glyph %u skipped, not grayscale\n", n);
334                         continue;
335                 }
336
337                 if(font->n_glyphs>=size)
338                 {
339                         size += 16;
340                         font->glyphs = (Glyph *)realloc(font->glyphs, size*sizeof(Glyph));
341                 }
342
343                 glyph = &font->glyphs[font->n_glyphs++];
344                 glyph->index = n;
345                 glyph->code = i;
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;
352
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 */
356                 if(bmp->pitch<0)
357                 {
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];
360                 }
361                 else
362                 {
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];
365                 }
366         }
367
368         if(verbose>=1)
369                 printf("Loaded %u glyphs\n", font->n_glyphs);
370
371         size = 0;
372         font->n_kerning = 0;
373         font->kerning = NULL;
374         for(i=0; i<font->n_glyphs; ++i) for(j=0; j<font->n_glyphs; ++j)
375                 if(j!=i)
376                 {
377                         FT_Vector kerning;
378                         FT_Get_Kerning(face, font->glyphs[i].index, font->glyphs[j].index, FT_KERNING_DEFAULT, &kerning);
379
380                         /* FreeType documentation says that vertical kerning is practically
381                         never used, so we ignore it. */
382                         if(kerning.x)
383                         {
384                                 Kerning *kern;
385
386                                 if(font->n_kerning>=size)
387                                 {
388                                         size += 16;
389                                         font->kerning = (Kerning *)realloc(font->kerning, size*sizeof(Kerning));
390                                 }
391
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;
396                         }
397                 }
398
399         if(verbose>=1)
400                 printf("Loaded %d kerning pairs\n", font->n_kerning);
401 }
402
403 void render_grid(Font *font, unsigned cellw, unsigned cellh, unsigned cpl, int seq)
404 {
405         unsigned i;
406         int      top = 0, bot = 0;
407         unsigned first, last;
408         unsigned maxw = 0, maxh = 0;
409
410         /* Find extremes of the glyph images. */
411         for(i=0; i<font->n_glyphs; ++i)
412         {
413                 int y;
414
415                 y = font->glyphs[i].offset_y+font->glyphs[i].image.h;
416                 if(y>top)
417                         top = y;
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;
424         }
425
426         if(cellw==0)
427         {
428                 /* Establish a large enough cell to hold all glyphs in the range. */
429                 int square = (cellh==cellw);
430                 cellw = maxw;
431                 cellh = top-bot;
432                 if(square)
433                 {
434                         if(cellh>cellw)
435                                 cellw = cellh;
436                         else
437                                 cellh = cellw;
438                 }
439         }
440
441         if(verbose>=1)
442         {
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");
448         }
449
450         if(cpl==0)
451         {
452                 /* Determine number of characters per line, trying to fit all the glyphs
453                 in a square image. */
454                 for(i=1;; i<<=1)
455                 {
456                         cpl = i/cellw;
457                         if(cpl>0 && font->n_glyphs/cpl*cellh<=cpl*cellw)
458                                 break;
459                 }
460         }
461
462         first = font->glyphs[0].code;
463         if(!seq)
464                 first -= first%cpl;
465         last = font->glyphs[font->n_glyphs-1].code;
466
467         font->image.w = round_to_pot(cpl*cellw);
468         font->image.h = round_to_pot((last-first+cpl)/cpl*cellh);
469
470         font->image.data = (char *)malloc(font->image.w*font->image.h);
471         memset(font->image.data, 255, font->image.w*font->image.h);
472
473         for(i=0; i<font->n_glyphs; ++i)
474         {
475                 Glyph    *glyph;
476                 int      ci, cx, cy;
477                 unsigned x, y;
478
479                 glyph = &font->glyphs[i];
480
481                 if(seq)
482                         ci = i;
483                 else
484                         ci = glyph->code-first;
485
486                 cx = (ci%cpl)*cellw;
487                 cy = (ci/cpl)*cellh;
488
489                 if(cellw>glyph->image.w)
490                         cx += (cellw-glyph->image.w)/2;
491                 cy += top-glyph->offset_y-glyph->image.h;
492
493                 glyph->x = cx;
494                 glyph->y = cy;
495
496                 for(y=0; y<glyph->image.h; ++y) for(x=0; x<glyph->image.w; ++x)
497                 {
498                         if(cx+x<0 || cx+x>=font->image.w || cy+y<0 || cy+y>=font->image.h)
499                                 continue;
500                         font->image.data[cx+x+(cy+y)*font->image.w] = 255-glyph->image.data[x+y*glyph->image.w];
501                 }
502         }
503 }
504
505 void render_packed(Font *font)
506 {
507         unsigned i;
508         unsigned area = 0;
509         char     *used_glyphs;
510         unsigned *used_pixels;
511         unsigned cx = 0, cy;
512         unsigned used_h = 0;
513
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);
517
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)
521         {
522                 font->image.h = (area*5/4)/font->image.w;
523                 if(font->image.h<=font->image.w)
524                         break;
525         }
526         font->image.h = round_to_pot(font->image.h);
527
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
531         column. */
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);
538
539         for(cy=0; cy<font->image.h;)
540         {
541                 unsigned w;
542                 unsigned x, y;
543                 Glyph    *glyph = NULL;
544                 unsigned best_score = 0;
545                 unsigned target_h = 0;
546
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;
552
553                 if(cx>=font->image.w)
554                 {
555                         cx = 0;
556                         ++cy;
557                         continue;
558                 }
559
560                 /* Count the free pixel at this position. */
561                 for(w=0; (cx+w<font->image.w && used_pixels[cx+w]<=cy); ++w) ;
562
563                 /* Find a suitable glyph to put here. */
564                 for(i=0; i<font->n_glyphs; ++i)
565                 {
566                         Glyph *g;
567
568                         g = &font->glyphs[i];
569                         if(!used_glyphs[i] && g->image.w<=w)
570                         {
571                                 unsigned score;
572
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)
578                                         score *= g->image.w;
579                                 else
580                                         score += g->image.w;
581
582                                 if(score>best_score)
583                                 {
584                                         glyph = g;
585                                         best_score = score;
586                                 }
587                         }
588                 }
589
590                 if(!glyph)
591                 {
592                         cx += w;
593                         continue;
594                 }
595
596                 used_glyphs[glyph-font->glyphs] = 1;
597                 glyph->x = cx;
598                 glyph->y = cy;
599
600                 for(y=0; y<glyph->image.h; ++y) for(x=0; x<glyph->image.w; ++x)
601                 {
602                         if(cx+x<0 || cx+x>=font->image.w || cy+y<0 || cy+y>=font->image.h)
603                                 continue;
604                         font->image.data[cx+x+(cy+y)*font->image.w] = 255-glyph->image.data[x+y*glyph->image.w];
605                 }
606                 for(x=0; x<glyph->image.w+2; ++x)
607                 {
608                         if(cx+x<1 || cx+x>font->image.w)
609                                 continue;
610                         if(used_pixels[cx+x-1]<cy+glyph->image.h+1)
611                                 used_pixels[cx+x-1] = cy+glyph->image.h+1;
612                 }
613
614                 if(cy+glyph->image.h>used_h)
615                         used_h = cy+glyph->image.h;
616         }
617
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);
621 }
622
623 int save_defs(const char *fn, const Font *font)
624 {
625         FILE     *out;
626         unsigned i;
627
628         out = fopen(fn, "w");
629         if(!out)
630         {
631                 fprintf(stderr, "Couldn't open %s\n",fn);
632                 return -1;
633         }
634
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)
641         {
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);
644         }
645         fprintf(out, "\n# Kerning info:\n");
646         fprintf(out, "# left right distance\n");
647         for(i=0; i<font->n_kerning; ++i)
648         {
649                 const Kerning *k = &font->kerning[i];
650                 fprintf(out, "kern %u %u %d\n", k->left_code, k->right_code, k->distance);
651         }
652
653         fclose(out);
654
655         return 0;
656 }
657
658 int save_png(const char *fn, const Image *image, char alpha)
659 {
660         FILE       *out;
661         png_struct *pngs;
662         png_info   *pngi;
663         png_byte   *rows[image->h];
664         int        i;
665         png_byte   *data2;
666         int        color;
667
668         if(!strcmp(fn, "-"))
669                 out = stdout;
670         else
671         {
672                 out = fopen(fn, "wb");
673                 if(!out)
674                 {
675                         fprintf(stderr, "Couldn't open %s\n",fn);
676                         return -1;
677                 }
678         }
679
680         pngs = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
681         if(!pngs)
682         {
683                 fprintf(stderr, "Error writing PNG file\n");
684                 return -1;
685         }
686         pngi = png_create_info_struct(pngs);
687         if(!pngi)
688         {
689                 png_destroy_write_struct(&pngs, NULL);
690                 fprintf(stderr, "Error writing PNG file\n");
691                 return -1;
692         }
693
694         png_init_io(pngs, out);
695         if(alpha)
696         {
697                 data2 = (png_byte *)malloc(image->w*image->h*2);
698                 for(i=0; i<image->w*image->h; ++i)
699                 {
700                         data2[i*2] = 255;
701                         data2[i*2+1] = 255-image->data[i];
702                 }
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;
706         }
707         else
708         {
709                 for(i=0; i<image->h; ++i)
710                         rows[i] = (png_byte *)(image->data+i*image->w);
711                 color = PNG_COLOR_TYPE_GRAY;
712         }
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);
717         if(alpha)
718                 free(data2);
719
720         if(verbose)
721                 printf("Saved %dx%d PNG image to %s\n", image->w, image->h, fn);
722
723         fclose(out);
724
725         return 0;
726 }