]> git.tdb.fi Git - ttf2png.git/blob - ttf2png.c
Correctly interpret a single number given to -c
[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                                 else
163                                         cellh = cellw;
164                         }
165                         break;
166                 case 'o':
167                         out_fn = optarg;
168                         break;
169                 case 'a':
170                         autohinter = 1;
171                         break;
172                 case 't':
173                         alpha = 1;
174                         break;
175                 case 'v':
176                         ++verbose;
177                         break;
178                 case 'h':
179                 case '?':
180                         usage();
181                         return 0;
182                 case 'e':
183                         seq = 1;
184                         break;
185                 case 'd':
186                         def_fn = optarg;
187                         break;
188                 case 'p':
189                         pack = 1;
190                         break;
191                 case 'i':
192                         invert = 1;
193                         break;
194                 }
195         }
196         if(!strcmp(out_fn, "-"))
197                 verbose = 0;
198
199         if(optind!=argc-1)
200         {
201                 usage();
202                 return 1;
203         }
204
205         fn = argv[optind];
206
207         err = FT_Init_FreeType(&freetype);
208         if(err)
209         {
210                 fprintf(stderr, "Couldn't initialize FreeType library\n");
211                 return 1;
212         }
213
214         err = FT_New_Face(freetype, fn, 0, &face);
215         if(err)
216         {
217                 fprintf(stderr, "Couldn't load font file\n");
218                 if(err==FT_Err_Unknown_File_Format)
219                         fprintf(stderr, "Unknown file format\n");
220                 return 1;
221         }
222
223         if(verbose)
224         {
225                 const char *name = FT_Get_Postscript_Name(face);
226                 printf("Font name: %s\n", name);
227                 printf("Glyphs:    %ld\n", face->num_glyphs);
228         }
229
230         err = FT_Set_Pixel_Sizes(face, 0, size);
231         if(err)
232         {
233                 fprintf(stderr, "Couldn't set size\n");
234                 return 1;
235         }
236
237         font.size = size;
238         init_font(&font, face, begin, end, autohinter);
239         if(pack)
240                 render_packed(&font);
241         else
242                 render_grid(&font, cellw, cellh, cpl, seq);
243         if(invert)
244         {
245                 for(i=0; (unsigned)i<font.image.w*font.image.h; ++i)
246                         font.image.data[i] = 255-font.image.data[i];
247         }
248         save_png(out_fn, &font.image, alpha);
249         if(def_fn)
250                 save_defs(def_fn, &font);
251
252         for(i=0; (unsigned)i<font.n_glyphs; ++i)
253                 free(font.glyphs[i].image.data);
254         free(font.glyphs);
255         free(font.image.data);
256
257         FT_Done_Face(face);
258         FT_Done_FreeType(freetype);
259
260         return 0;
261 }
262
263 unsigned round_to_pot(unsigned n)
264 {
265         n -= 1;
266         n |= n>>1;
267         n |= n>>2;
268         n |= n>>4;
269         n |= n>>8;
270         n |= n>>16;
271
272         return n+1;
273 }
274
275 void usage()
276 {
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");
280
281         printf("Usage: ttf2png [options] <TTF file>\n\n");
282
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");
297 }
298
299 void init_font(Font *font, FT_Face face, unsigned first, unsigned last, int autohinter)
300 {
301         unsigned i, j;
302         unsigned size = 0;
303
304         font->ascent = (face->size->metrics.ascender+63)>>6;
305         font->descent = (face->size->metrics.descender+63)>>6;
306
307         if(verbose>=1)
308         {
309                 printf("Ascent:    %d\n", font->ascent);
310                 printf("Descent:   %d\n", font->descent);
311         }
312
313         font->n_glyphs = 0;
314         font->glyphs = NULL;
315         for(i=first; i<=last; ++i)
316         {
317                 unsigned  n;
318                 FT_Bitmap *bmp = &face->glyph->bitmap;
319                 int       x, y;
320                 int       flags = 0;
321                 Glyph     *glyph;
322
323                 n = FT_Get_Char_Index(face, i);
324                 if(!n)
325                         continue;
326
327                 if(autohinter)
328                         flags |= FT_LOAD_FORCE_AUTOHINT;
329                 FT_Load_Glyph(face, n, flags);
330                 FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
331
332                 if(verbose>=2)
333                         printf("  Char %u: glyph %u, size %dx%d\n", i, n, bmp->width, bmp->rows);
334
335                 if(bmp->pixel_mode!=FT_PIXEL_MODE_GRAY)
336                 {
337                         fprintf(stderr, "Warning: Glyph %u skipped, not grayscale\n", n);
338                         continue;
339                 }
340
341                 if(font->n_glyphs>=size)
342                 {
343                         size += 16;
344                         font->glyphs = (Glyph *)realloc(font->glyphs, size*sizeof(Glyph));
345                 }
346
347                 glyph = &font->glyphs[font->n_glyphs++];
348                 glyph->index = n;
349                 glyph->code = i;
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;
356
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 */
360                 if(bmp->pitch<0)
361                 {
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];
364                 }
365                 else
366                 {
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];
369                 }
370         }
371
372         if(verbose>=1)
373                 printf("Loaded %u glyphs\n", font->n_glyphs);
374
375         size = 0;
376         font->n_kerning = 0;
377         font->kerning = NULL;
378         for(i=0; i<font->n_glyphs; ++i) for(j=0; j<font->n_glyphs; ++j)
379                 if(j!=i)
380                 {
381                         FT_Vector kerning;
382                         FT_Get_Kerning(face, font->glyphs[i].index, font->glyphs[j].index, FT_KERNING_DEFAULT, &kerning);
383
384                         /* FreeType documentation says that vertical kerning is practically
385                         never used, so we ignore it. */
386                         if(kerning.x)
387                         {
388                                 Kerning *kern;
389
390                                 if(font->n_kerning>=size)
391                                 {
392                                         size += 16;
393                                         font->kerning = (Kerning *)realloc(font->kerning, size*sizeof(Kerning));
394                                 }
395
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;
400                         }
401                 }
402
403         if(verbose>=1)
404                 printf("Loaded %d kerning pairs\n", font->n_kerning);
405 }
406
407 void render_grid(Font *font, unsigned cellw, unsigned cellh, unsigned cpl, int seq)
408 {
409         unsigned i;
410         int      top = 0, bot = 0;
411         unsigned first, last;
412         unsigned maxw = 0, maxh = 0;
413
414         /* Find extremes of the glyph images. */
415         for(i=0; i<font->n_glyphs; ++i)
416         {
417                 int y;
418
419                 y = font->glyphs[i].offset_y+font->glyphs[i].image.h;
420                 if(y>top)
421                         top = y;
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;
428         }
429
430         if(cellw==0)
431         {
432                 /* Establish a large enough cell to hold all glyphs in the range. */
433                 int square = (cellh==cellw);
434                 cellw = maxw;
435                 cellh = top-bot;
436                 if(square)
437                 {
438                         if(cellh>cellw)
439                                 cellw = cellh;
440                         else
441                                 cellh = cellw;
442                 }
443         }
444
445         if(verbose>=1)
446         {
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");
452         }
453
454         if(cpl==0)
455         {
456                 /* Determine number of characters per line, trying to fit all the glyphs
457                 in a square image. */
458                 for(i=1;; i<<=1)
459                 {
460                         cpl = i/cellw;
461                         if(cpl>0 && font->n_glyphs/cpl*cellh<=cpl*cellw)
462                                 break;
463                 }
464         }
465
466         first = font->glyphs[0].code;
467         if(!seq)
468                 first -= first%cpl;
469         last = font->glyphs[font->n_glyphs-1].code;
470
471         font->image.w = round_to_pot(cpl*cellw);
472         font->image.h = round_to_pot((last-first+cpl)/cpl*cellh);
473
474         font->image.data = (char *)malloc(font->image.w*font->image.h);
475         memset(font->image.data, 255, font->image.w*font->image.h);
476
477         for(i=0; i<font->n_glyphs; ++i)
478         {
479                 Glyph    *glyph;
480                 unsigned ci, cx, cy;
481                 unsigned x, y;
482
483                 glyph = &font->glyphs[i];
484
485                 if(seq)
486                         ci = i;
487                 else
488                         ci = glyph->code-first;
489
490                 cx = (ci%cpl)*cellw;
491                 cy = (ci/cpl)*cellh;
492
493                 if(cellw>glyph->image.w)
494                         cx += (cellw-glyph->image.w)/2;
495                 cy += top-glyph->offset_y-glyph->image.h;
496
497                 glyph->x = cx;
498                 glyph->y = cy;
499
500                 for(y=0; y<glyph->image.h; ++y) for(x=0; x<glyph->image.w; ++x)
501                 {
502                         if(cx+x>=font->image.w || cy+y>=font->image.h)
503                                 continue;
504                         font->image.data[cx+x+(cy+y)*font->image.w] = 255-glyph->image.data[x+y*glyph->image.w];
505                 }
506         }
507 }
508
509 void render_packed(Font *font)
510 {
511         unsigned i;
512         unsigned area = 0;
513         char     *used_glyphs;
514         unsigned *used_pixels;
515         unsigned cx = 0, cy;
516         unsigned used_h = 0;
517
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);
521
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)
525         {
526                 font->image.h = (area*5/4)/font->image.w;
527                 if(font->image.h<=font->image.w)
528                         break;
529         }
530         font->image.h = round_to_pot(font->image.h);
531
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
535         column. */
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);
542
543         for(cy=0; cy<font->image.h;)
544         {
545                 unsigned w;
546                 unsigned x, y;
547                 Glyph    *glyph = NULL;
548                 unsigned best_score = 0;
549                 unsigned target_h = 0;
550
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;
556
557                 if(cx>=font->image.w)
558                 {
559                         cx = 0;
560                         ++cy;
561                         continue;
562                 }
563
564                 /* Count the free pixel at this position. */
565                 for(w=0; (cx+w<font->image.w && used_pixels[cx+w]<=cy); ++w) ;
566
567                 /* Find a suitable glyph to put here. */
568                 for(i=0; i<font->n_glyphs; ++i)
569                 {
570                         Glyph *g;
571
572                         g = &font->glyphs[i];
573                         if(!used_glyphs[i] && g->image.w<=w)
574                         {
575                                 unsigned score;
576
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)
582                                         score *= g->image.w;
583                                 else
584                                         score += g->image.w;
585
586                                 if(score>best_score)
587                                 {
588                                         glyph = g;
589                                         best_score = score;
590                                 }
591                         }
592                 }
593
594                 if(!glyph)
595                 {
596                         cx += w;
597                         continue;
598                 }
599
600                 used_glyphs[glyph-font->glyphs] = 1;
601                 glyph->x = cx;
602                 glyph->y = cy;
603
604                 for(y=0; y<glyph->image.h; ++y) for(x=0; x<glyph->image.w; ++x)
605                 {
606                         if(cx+x>=font->image.w || cy+y>=font->image.h)
607                                 continue;
608                         font->image.data[cx+x+(cy+y)*font->image.w] = 255-glyph->image.data[x+y*glyph->image.w];
609                 }
610                 for(x=0; x<glyph->image.w+2; ++x)
611                 {
612                         if(cx+x<1 || cx+x>font->image.w)
613                                 continue;
614                         if(used_pixels[cx+x-1]<cy+glyph->image.h+1)
615                                 used_pixels[cx+x-1] = cy+glyph->image.h+1;
616                 }
617
618                 if(cy+glyph->image.h>used_h)
619                         used_h = cy+glyph->image.h;
620         }
621
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);
625 }
626
627 int save_defs(const char *fn, const Font *font)
628 {
629         FILE     *out;
630         unsigned i;
631
632         out = fopen(fn, "w");
633         if(!out)
634         {
635                 fprintf(stderr, "Couldn't open %s\n",fn);
636                 return -1;
637         }
638
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)
645         {
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);
648         }
649         fprintf(out, "\n# Kerning info:\n");
650         fprintf(out, "# left right distance\n");
651         for(i=0; i<font->n_kerning; ++i)
652         {
653                 const Kerning *k = &font->kerning[i];
654                 fprintf(out, "kern %u %u %d\n", k->left_code, k->right_code, k->distance);
655         }
656
657         fclose(out);
658
659         return 0;
660 }
661
662 int save_png(const char *fn, const Image *image, char alpha)
663 {
664         FILE       *out;
665         png_struct *pngs;
666         png_info   *pngi;
667         png_byte   **rows;
668         unsigned   i;
669         png_byte   *data2;
670         int        color;
671
672         if(!strcmp(fn, "-"))
673                 out = stdout;
674         else
675         {
676                 out = fopen(fn, "wb");
677                 if(!out)
678                 {
679                         fprintf(stderr, "Couldn't open %s\n",fn);
680                         return -1;
681                 }
682         }
683
684         pngs = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
685         if(!pngs)
686         {
687                 fprintf(stderr, "Error writing PNG file\n");
688                 return -1;
689         }
690         pngi = png_create_info_struct(pngs);
691         if(!pngi)
692         {
693                 png_destroy_write_struct(&pngs, NULL);
694                 fprintf(stderr, "Error writing PNG file\n");
695                 return -1;
696         }
697
698         png_init_io(pngs, out);
699         rows = (png_byte **)malloc(image->h*sizeof(png_byte *));
700         if(alpha)
701         {
702                 data2 = (png_byte *)malloc(image->w*image->h*2);
703                 for(i=0; i<image->w*image->h; ++i)
704                 {
705                         data2[i*2] = 255;
706                         data2[i*2+1] = 255-image->data[i];
707                 }
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;
711         }
712         else
713         {
714                 for(i=0; i<image->h; ++i)
715                         rows[i] = (png_byte *)(image->data+i*image->w);
716                 color = PNG_COLOR_TYPE_GRAY;
717         }
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);
722         free(rows);
723         if(alpha)
724                 free(data2);
725
726         if(verbose)
727                 printf("Saved %dx%d PNG image to %s\n", image->w, image->h, fn);
728
729         fclose(out);
730
731         return 0;
732 }