]> git.tdb.fi Git - ttf2png.git/blob - ttf2png.c
Use stricter warnings and make the code compile with them
[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; (unsigned)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; (unsigned)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
279         printf("Usage: ttf2png [options] <TTF file>\n\n");
280
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");
295 }
296
297 void init_font(Font *font, FT_Face face, unsigned first, unsigned last, int autohinter)
298 {
299         unsigned i, j;
300         unsigned size = 0;
301
302         font->ascent = (face->size->metrics.ascender+63)>>6;
303         font->descent = (face->size->metrics.descender+63)>>6;
304
305         if(verbose>=1)
306         {
307                 printf("Ascent:    %d\n", font->ascent);
308                 printf("Descent:   %d\n", font->descent);
309         }
310
311         font->n_glyphs = 0;
312         font->glyphs = NULL;
313         for(i=first; i<=last; ++i)
314         {
315                 unsigned  n;
316                 FT_Bitmap *bmp = &face->glyph->bitmap;
317                 int       x, y;
318                 int       flags = 0;
319                 Glyph     *glyph;
320
321                 n = FT_Get_Char_Index(face, i);
322                 if(!n)
323                         continue;
324
325                 if(autohinter)
326                         flags |= FT_LOAD_FORCE_AUTOHINT;
327                 FT_Load_Glyph(face, n, flags);
328                 FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
329
330                 if(verbose>=2)
331                         printf("  Char %u: glyph %u, size %dx%d\n", i, n, bmp->width, bmp->rows);
332
333                 if(bmp->pixel_mode!=FT_PIXEL_MODE_GRAY)
334                 {
335                         fprintf(stderr, "Warning: Glyph %u skipped, not grayscale\n", n);
336                         continue;
337                 }
338
339                 if(font->n_glyphs>=size)
340                 {
341                         size += 16;
342                         font->glyphs = (Glyph *)realloc(font->glyphs, size*sizeof(Glyph));
343                 }
344
345                 glyph = &font->glyphs[font->n_glyphs++];
346                 glyph->index = n;
347                 glyph->code = i;
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;
354
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 */
358                 if(bmp->pitch<0)
359                 {
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];
362                 }
363                 else
364                 {
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];
367                 }
368         }
369
370         if(verbose>=1)
371                 printf("Loaded %u glyphs\n", font->n_glyphs);
372
373         size = 0;
374         font->n_kerning = 0;
375         font->kerning = NULL;
376         for(i=0; i<font->n_glyphs; ++i) for(j=0; j<font->n_glyphs; ++j)
377                 if(j!=i)
378                 {
379                         FT_Vector kerning;
380                         FT_Get_Kerning(face, font->glyphs[i].index, font->glyphs[j].index, FT_KERNING_DEFAULT, &kerning);
381
382                         /* FreeType documentation says that vertical kerning is practically
383                         never used, so we ignore it. */
384                         if(kerning.x)
385                         {
386                                 Kerning *kern;
387
388                                 if(font->n_kerning>=size)
389                                 {
390                                         size += 16;
391                                         font->kerning = (Kerning *)realloc(font->kerning, size*sizeof(Kerning));
392                                 }
393
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;
398                         }
399                 }
400
401         if(verbose>=1)
402                 printf("Loaded %d kerning pairs\n", font->n_kerning);
403 }
404
405 void render_grid(Font *font, unsigned cellw, unsigned cellh, unsigned cpl, int seq)
406 {
407         unsigned i;
408         int      top = 0, bot = 0;
409         unsigned first, last;
410         unsigned maxw = 0, maxh = 0;
411
412         /* Find extremes of the glyph images. */
413         for(i=0; i<font->n_glyphs; ++i)
414         {
415                 int y;
416
417                 y = font->glyphs[i].offset_y+font->glyphs[i].image.h;
418                 if(y>top)
419                         top = y;
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;
426         }
427
428         if(cellw==0)
429         {
430                 /* Establish a large enough cell to hold all glyphs in the range. */
431                 int square = (cellh==cellw);
432                 cellw = maxw;
433                 cellh = top-bot;
434                 if(square)
435                 {
436                         if(cellh>cellw)
437                                 cellw = cellh;
438                         else
439                                 cellh = cellw;
440                 }
441         }
442
443         if(verbose>=1)
444         {
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");
450         }
451
452         if(cpl==0)
453         {
454                 /* Determine number of characters per line, trying to fit all the glyphs
455                 in a square image. */
456                 for(i=1;; i<<=1)
457                 {
458                         cpl = i/cellw;
459                         if(cpl>0 && font->n_glyphs/cpl*cellh<=cpl*cellw)
460                                 break;
461                 }
462         }
463
464         first = font->glyphs[0].code;
465         if(!seq)
466                 first -= first%cpl;
467         last = font->glyphs[font->n_glyphs-1].code;
468
469         font->image.w = round_to_pot(cpl*cellw);
470         font->image.h = round_to_pot((last-first+cpl)/cpl*cellh);
471
472         font->image.data = (char *)malloc(font->image.w*font->image.h);
473         memset(font->image.data, 255, font->image.w*font->image.h);
474
475         for(i=0; i<font->n_glyphs; ++i)
476         {
477                 Glyph    *glyph;
478                 unsigned ci, cx, cy;
479                 unsigned x, y;
480
481                 glyph = &font->glyphs[i];
482
483                 if(seq)
484                         ci = i;
485                 else
486                         ci = glyph->code-first;
487
488                 cx = (ci%cpl)*cellw;
489                 cy = (ci/cpl)*cellh;
490
491                 if(cellw>glyph->image.w)
492                         cx += (cellw-glyph->image.w)/2;
493                 cy += top-glyph->offset_y-glyph->image.h;
494
495                 glyph->x = cx;
496                 glyph->y = cy;
497
498                 for(y=0; y<glyph->image.h; ++y) for(x=0; x<glyph->image.w; ++x)
499                 {
500                         if(cx+x>=font->image.w || cy+y>=font->image.h)
501                                 continue;
502                         font->image.data[cx+x+(cy+y)*font->image.w] = 255-glyph->image.data[x+y*glyph->image.w];
503                 }
504         }
505 }
506
507 void render_packed(Font *font)
508 {
509         unsigned i;
510         unsigned area = 0;
511         char     *used_glyphs;
512         unsigned *used_pixels;
513         unsigned cx = 0, cy;
514         unsigned used_h = 0;
515
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);
519
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)
523         {
524                 font->image.h = (area*5/4)/font->image.w;
525                 if(font->image.h<=font->image.w)
526                         break;
527         }
528         font->image.h = round_to_pot(font->image.h);
529
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
533         column. */
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);
540
541         for(cy=0; cy<font->image.h;)
542         {
543                 unsigned w;
544                 unsigned x, y;
545                 Glyph    *glyph = NULL;
546                 unsigned best_score = 0;
547                 unsigned target_h = 0;
548
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;
554
555                 if(cx>=font->image.w)
556                 {
557                         cx = 0;
558                         ++cy;
559                         continue;
560                 }
561
562                 /* Count the free pixel at this position. */
563                 for(w=0; (cx+w<font->image.w && used_pixels[cx+w]<=cy); ++w) ;
564
565                 /* Find a suitable glyph to put here. */
566                 for(i=0; i<font->n_glyphs; ++i)
567                 {
568                         Glyph *g;
569
570                         g = &font->glyphs[i];
571                         if(!used_glyphs[i] && g->image.w<=w)
572                         {
573                                 unsigned score;
574
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)
580                                         score *= g->image.w;
581                                 else
582                                         score += g->image.w;
583
584                                 if(score>best_score)
585                                 {
586                                         glyph = g;
587                                         best_score = score;
588                                 }
589                         }
590                 }
591
592                 if(!glyph)
593                 {
594                         cx += w;
595                         continue;
596                 }
597
598                 used_glyphs[glyph-font->glyphs] = 1;
599                 glyph->x = cx;
600                 glyph->y = cy;
601
602                 for(y=0; y<glyph->image.h; ++y) for(x=0; x<glyph->image.w; ++x)
603                 {
604                         if(cx+x>=font->image.w || cy+y>=font->image.h)
605                                 continue;
606                         font->image.data[cx+x+(cy+y)*font->image.w] = 255-glyph->image.data[x+y*glyph->image.w];
607                 }
608                 for(x=0; x<glyph->image.w+2; ++x)
609                 {
610                         if(cx+x<1 || cx+x>font->image.w)
611                                 continue;
612                         if(used_pixels[cx+x-1]<cy+glyph->image.h+1)
613                                 used_pixels[cx+x-1] = cy+glyph->image.h+1;
614                 }
615
616                 if(cy+glyph->image.h>used_h)
617                         used_h = cy+glyph->image.h;
618         }
619
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);
623 }
624
625 int save_defs(const char *fn, const Font *font)
626 {
627         FILE     *out;
628         unsigned i;
629
630         out = fopen(fn, "w");
631         if(!out)
632         {
633                 fprintf(stderr, "Couldn't open %s\n",fn);
634                 return -1;
635         }
636
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)
643         {
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);
646         }
647         fprintf(out, "\n# Kerning info:\n");
648         fprintf(out, "# left right distance\n");
649         for(i=0; i<font->n_kerning; ++i)
650         {
651                 const Kerning *k = &font->kerning[i];
652                 fprintf(out, "kern %u %u %d\n", k->left_code, k->right_code, k->distance);
653         }
654
655         fclose(out);
656
657         return 0;
658 }
659
660 int save_png(const char *fn, const Image *image, char alpha)
661 {
662         FILE       *out;
663         png_struct *pngs;
664         png_info   *pngi;
665         png_byte   **rows;
666         unsigned   i;
667         png_byte   *data2;
668         int        color;
669
670         if(!strcmp(fn, "-"))
671                 out = stdout;
672         else
673         {
674                 out = fopen(fn, "wb");
675                 if(!out)
676                 {
677                         fprintf(stderr, "Couldn't open %s\n",fn);
678                         return -1;
679                 }
680         }
681
682         pngs = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
683         if(!pngs)
684         {
685                 fprintf(stderr, "Error writing PNG file\n");
686                 return -1;
687         }
688         pngi = png_create_info_struct(pngs);
689         if(!pngi)
690         {
691                 png_destroy_write_struct(&pngs, NULL);
692                 fprintf(stderr, "Error writing PNG file\n");
693                 return -1;
694         }
695
696         png_init_io(pngs, out);
697         rows = (png_byte **)malloc(image->h*sizeof(png_byte *));
698         if(alpha)
699         {
700                 data2 = (png_byte *)malloc(image->w*image->h*2);
701                 for(i=0; i<image->w*image->h; ++i)
702                 {
703                         data2[i*2] = 255;
704                         data2[i*2+1] = 255-image->data[i];
705                 }
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;
709         }
710         else
711         {
712                 for(i=0; i<image->h; ++i)
713                         rows[i] = (png_byte *)(image->data+i*image->w);
714                 color = PNG_COLOR_TYPE_GRAY;
715         }
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);
720         free(rows);
721         if(alpha)
722                 free(data2);
723
724         if(verbose)
725                 printf("Saved %dx%d PNG image to %s\n", image->w, image->h, fn);
726
727         fclose(out);
728
729         return 0;
730 }