]> git.tdb.fi Git - ttf2png.git/blob - ttf2png.c
Better documentation
[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 code;
37         Image    image;
38         unsigned x, y;
39         int      offset_x;
40         int      offset_y;
41         int      advance;
42 } Glyph;
43
44 typedef struct sFont
45 {
46         unsigned size;
47         int      ascent;
48         int      descent;
49         unsigned n_glyphs;
50         Glyph    *glyphs;
51         Image    image;
52 } Font;
53
54 unsigned round_to_pot(unsigned);
55 void usage();
56 void init_font(Font *, FT_Face, unsigned, unsigned, int);
57 void render_grid(Font *, unsigned, unsigned, unsigned, int);
58 void render_packed(Font *);
59 int save_defs(const char *, const Font *);
60 int save_png(const char *, const Image *, char);
61
62 char verbose = 0;
63
64 int main(int argc, char **argv)
65 {
66         char *fn;
67         int  begin = 0;
68         int  end = 255;
69         int  size = 10;
70         int  cpl = 0;
71         int  cellw = 0;
72         int  cellh = 0;
73         char autohinter = 0;
74         char seq = 0;
75         char alpha = 0;
76         char invert = 0;
77         char pack = 0;
78
79         FT_Library freetype;
80         FT_Face    face;
81
82         int  err;
83         int  i;
84
85         char *out_fn = "font.png";
86         char *def_fn = NULL;
87
88         Font font;
89
90         if(argc<2)
91         {
92                 usage();
93                 return 1;
94         }
95
96         while((i = getopt(argc, argv, "r:s:l:c:o:atvh?ed:pi")) != -1)
97         {
98                 char *ptr;
99                 int  temp;
100                 switch(i)
101                 {
102                 case 'r':
103                         if(!strcmp(optarg, "all"))
104                         {
105                                 begin = 0;
106                                 end = 0x110000;
107                         }
108                         else
109                         {
110                                 if(!isdigit(optarg[0]))
111                                         temp = -1;
112                                 else
113                                 {
114                                         temp = strtol(optarg, &ptr, 0);
115                                         if(ptr[0]!=',' || !isdigit(ptr[1]))
116                                                 temp = -1;
117                                 }
118                                 if(temp<0)
119                                 {
120                                         printf("Not a valid range: %s\n", optarg);
121                                         exit(1);
122                                 }
123                                 else
124                                 {
125                                         begin = temp;
126                                         end = strtol(ptr+1, NULL, 0);
127                                 }
128                         }
129                         break;
130                 case 's':
131                         size = strtol(optarg, NULL, 0);
132                         break;
133                 case 'l':
134                         cpl = strtol(optarg, NULL, 0);
135                         break;
136                 case 'c':
137                         if(!strcmp(optarg, "auto"))
138                         {
139                                 cellw = 0;
140                                 cellh = 0;
141                         }
142                         else if(!strcmp(optarg, "autorect"))
143                         {
144                                 cellw = 0;
145                                 cellh = 1;
146                         }
147                         else
148                         {
149                                 cellw = strtol(optarg, &ptr, 0);
150                                 if(ptr[0]=='x' && isdigit(ptr[1]))
151                                         cellh = strtol(ptr+1, NULL, 0);
152                         }
153                         break;
154                 case 'o':
155                         out_fn = optarg;
156                         break;
157                 case 'a':
158                         autohinter = 1;
159                         break;
160                 case 't':
161                         alpha = 1;
162                         break;
163                 case 'v':
164                         ++verbose;
165                         break;
166                 case 'h':
167                 case '?':
168                         usage();
169                         return 0;
170                 case 'e':
171                         seq = 1;
172                         break;
173                 case 'd':
174                         def_fn = optarg;
175                         break;
176                 case 'p':
177                         pack = 1;
178                         break;
179                 case 'i':
180                         invert = 1;
181                         break;
182                 }
183         }
184         if(!strcmp(out_fn, "-"))
185                 verbose = 0;
186
187         if(optind!=argc-1)
188         {
189                 usage();
190                 return 1;
191         }
192
193         fn = argv[optind];
194
195         err = FT_Init_FreeType(&freetype);
196         if(err)
197         {
198                 fprintf(stderr, "Couldn't initialize FreeType library\n");
199                 return 1;
200         }
201
202         err = FT_New_Face(freetype, fn, 0, &face);
203         if(err)
204         {
205                 fprintf(stderr, "Couldn't load font file\n");
206                 if(err==FT_Err_Unknown_File_Format)
207                         fprintf(stderr, "Unknown file format\n");
208                 return 1;
209         }
210
211         if(verbose)
212         {
213                 const char *name = FT_Get_Postscript_Name(face);
214                 printf("Font name: %s\n", name);
215                 printf("Glyphs:    %ld\n", face->num_glyphs);
216         }
217
218         err = FT_Set_Pixel_Sizes(face, 0, size);
219         if(err)
220         {
221                 fprintf(stderr, "Couldn't set size\n");
222                 return 1;
223         }
224
225         font.size = size;
226         init_font(&font, face, begin, end, autohinter);
227         if(pack)
228                 render_packed(&font);
229         else
230                 render_grid(&font, cellw, cellh, cpl, seq);
231         if(invert)
232         {
233                 for(i=0; i<font.image.w*font.image.h; ++i)
234                         font.image.data[i] = 255-font.image.data[i];
235         }
236         save_png(out_fn, &font.image, alpha);
237         if(def_fn)
238                 save_defs(def_fn, &font);
239
240         for(i=0; i<font.n_glyphs; ++i)
241                 free(font.glyphs[i].image.data);
242         free(font.glyphs);
243         free(font.image.data);
244
245         FT_Done_Face(face);
246         FT_Done_FreeType(freetype);
247
248         return 0;
249 }
250
251 unsigned round_to_pot(unsigned n)
252 {
253         n -= 1;
254         n |= n>>1;
255         n |= n>>2;
256         n |= n>>4;
257         n |= n>>8;
258         n |= n>>16;
259
260         return n+1;
261 }
262
263 void usage()
264 {
265         printf("ttf2png - True Type Font to PNG converter\n"
266                 "Copyright (c) 2004-2008  Mikko Rasa, Mikkosoft Productions\n"
267                 "Distributed under the GNU General Public License\n\n"
268                 "Usage: ttf2png [options] <TTF file>\n\n"
269                 "Accepted options (default values in [brackets])\n"
270                 "  -r  Range of characters to convert [0,255]\n"
271                 "  -s  Font size to use, in pixels [10]\n"
272                 "  -l  Number of characters to put in one line [auto]\n"
273                 "  -c  Character cell size, in pixels [auto]\n"
274                 "  -o  Output file name (or - for stdout) [font.png]\n"
275                 "  -a  Force autohinter\n"
276                 "  -t  Render glyphs to alpha channel\n"
277                 "  -i  Invert colors of the glyphs\n"
278                 "  -v  Increase the level of verbosity\n"
279                 "  -e  Use cells in sequence, without gaps\n"
280                 "  -p  Pack the glyphs tightly instead of in a grid\n"
281                 "  -d  File name for writing glyph definitions\n"
282                 "  -h  Print this message\n");
283 }
284
285 void init_font(Font *font, FT_Face face, unsigned first, unsigned last, int autohinter)
286 {
287         unsigned i;
288         unsigned size = 0;
289
290         font->ascent = (face->size->metrics.ascender+63)>>6;
291         font->descent = (face->size->metrics.descender+63)>>6;
292
293         if(verbose>=1)
294         {
295                 printf("Ascent:    %d\n", font->ascent);
296                 printf("Descent:   %d\n", font->descent);
297         }
298
299         font->n_glyphs = 0;
300         font->glyphs = NULL;
301         for(i=first; i<=last; ++i)
302         {
303                 unsigned  n;
304                 FT_Bitmap *bmp = &face->glyph->bitmap;
305                 int       x, y;
306                 int       flags = 0;
307                 Glyph     *glyph;
308
309                 n = FT_Get_Char_Index(face, i);
310                 if(!n)
311                         continue;
312
313                 if(autohinter)
314                         flags |= FT_LOAD_FORCE_AUTOHINT;
315                 FT_Load_Glyph(face, n, flags);
316                 FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
317
318                 if(verbose>=2)
319                         printf("  Char %u: glyph %u, size %dx%d\n", i, n, bmp->width, bmp->rows);
320
321                 if(bmp->pixel_mode!=FT_PIXEL_MODE_GRAY)
322                 {
323                         fprintf(stderr, "Warning: Glyph %u skipped, not grayscale\n", n);
324                         continue;
325                 }
326
327                 if(font->n_glyphs>=size)
328                 {
329                         size += 16;
330                         font->glyphs = (Glyph *)realloc(font->glyphs, size*sizeof(Glyph));
331                 }
332
333                 glyph = &font->glyphs[font->n_glyphs++];
334                 glyph->code = i;
335                 glyph->image.w = bmp->width;
336                 glyph->image.h = bmp->rows;
337                 glyph->image.data = (char *)malloc(bmp->width*bmp->rows);
338                 glyph->offset_x = face->glyph->bitmap_left;
339                 glyph->offset_y = face->glyph->bitmap_top-bmp->rows;
340                 glyph->advance = (int)(face->glyph->advance.x+32)/64;
341
342                 /* Copy the glyph image since FreeType uses a global buffer, which would
343                 be overwritten by the next glyph.  Negative pitch means the scanlines
344                 start from the bottom */
345                 if(bmp->pitch<0)
346                 {
347                         for(y=0; y<bmp->rows; ++y) for(x=0; x<bmp->width; ++x)
348                                 glyph->image.data[x+(glyph->image.h-1-y)*glyph->image.w] = bmp->buffer[x-y*bmp->pitch];
349                 }
350                 else
351                 {
352                         for(y=0; y<bmp->rows; ++y) for(x=0; x<bmp->width; ++x)
353                                 glyph->image.data[x+y*glyph->image.w] = bmp->buffer[x+y*bmp->pitch];
354                 }
355         }
356
357         if(verbose>=1)
358                 printf("Loaded %u glyphs\n", font->n_glyphs);
359 }
360
361 void render_grid(Font *font, unsigned cellw, unsigned cellh, unsigned cpl, int seq)
362 {
363         unsigned i;
364         int      top = 0, bot = 0;
365         unsigned first, last;
366         unsigned maxw = 0, maxh = 0;
367
368         /* Find extremes of the glyph images. */
369         for(i=0; i<font->n_glyphs; ++i)
370         {
371                 int y;
372
373                 y = font->glyphs[i].offset_y+font->glyphs[i].image.h;
374                 if(y>top)
375                         top = y;
376                 if(font->glyphs[i].offset_y<bot)
377                         bot = font->glyphs[i].offset_y;
378                 if(font->glyphs[i].image.w>maxw)
379                         maxw = font->glyphs[i].image.w;
380                 if(font->glyphs[i].image.h>maxh)
381                         maxh = font->glyphs[i].image.h;
382         }
383
384         if(cellw==0)
385         {
386                 /* Establish a large enough cell to hold all glyphs in the range. */
387                 int square = (cellh==cellw);
388                 cellw = maxw;
389                 cellh = top-bot;
390                 if(square)
391                 {
392                         if(cellh>cellw)
393                                 cellw = cellh;
394                         else
395                                 cellh = cellw;
396                 }
397         }
398
399         if(verbose>=1)
400         {
401                 printf("Max size:  %u x %u\n", maxw, maxh);
402                 printf("Y range:   [%d %d]\n", bot, top);
403                 printf("Cell size: %u x %u\n", cellw, cellh);
404                 if(maxw>cellw || top-bot>cellh)
405                         fprintf(stderr, "Warning: character size exceeds cell size\n");
406         }
407
408         if(cpl==0)
409         {
410                 /* Determine number of characters per line, trying to fit all the glyphs
411                 in a square image. */
412                 for(i=1;; i<<=1)
413                 {
414                         cpl = i/cellw;
415                         if(cpl>0 && font->n_glyphs/cpl*cellh<=cpl*cellw)
416                                 break;
417                 }
418         }
419
420         first = font->glyphs[0].code;
421         if(!seq)
422                 first -= first%cpl;
423         last = font->glyphs[font->n_glyphs-1].code;
424
425         font->image.w = round_to_pot(cpl*cellw);
426         font->image.h = round_to_pot((last-first+cpl)/cpl*cellh);
427
428         font->image.data = (char *)malloc(font->image.w*font->image.h);
429         memset(font->image.data, 255, font->image.w*font->image.h);
430
431         for(i=0; i<font->n_glyphs; ++i)
432         {
433                 Glyph    *glyph;
434                 int      ci, cx, cy;
435                 unsigned x, y;
436
437                 glyph = &font->glyphs[i];
438
439                 if(seq)
440                         ci = i;
441                 else
442                         ci = glyph->code-first;
443
444                 cx = (ci%cpl)*cellw;
445                 cy = (ci/cpl)*cellh;
446
447                 if(cellw>glyph->image.w)
448                         cx += (cellw-glyph->image.w)/2;
449                 cy += top-glyph->offset_y-glyph->image.h;
450
451                 glyph->x = cx;
452                 glyph->y = cy;
453
454                 for(y=0; y<glyph->image.h; ++y) for(x=0; x<glyph->image.w; ++x)
455                 {
456                         if(cx+x<0 || cx+x>=font->image.w || cy+y<0 || cy+y>=font->image.h)
457                                 continue;
458                         font->image.data[cx+x+(cy+y)*font->image.w] = 255-glyph->image.data[x+y*glyph->image.w];
459                 }
460         }
461 }
462
463 void render_packed(Font *font)
464 {
465         unsigned i;
466         unsigned area = 0;
467         char     *used_glyphs;
468         unsigned *used_pixels;
469         unsigned cx = 0, cy;
470         unsigned used_h = 0;
471
472         /* Compute the total area occupied by glyphs and padding. */
473         for(i=0; i<font->n_glyphs; ++i)
474                 area += (font->glyphs[i].image.w+1)*(font->glyphs[i].image.h+1);
475
476         /* Find an image size that's no higher than wide, allowing for some
477         imperfections in the packing. */
478         for(font->image.w=1;; font->image.w<<=1)
479         {
480                 font->image.h = (area*5/4)/font->image.w;
481                 if(font->image.h<=font->image.w)
482                         break;
483         }
484         font->image.h = round_to_pot(font->image.h);
485
486         /* Allocate arrays for storing the image and keeping track of used pixels and
487         glyphs.  Since glyphs are rectangular and the image is filled starting from
488         the top, it's enough to track the number of used pixels at the top of each
489         column. */
490         font->image.data = (char *)malloc(font->image.w*font->image.h);
491         memset(font->image.data, 255, font->image.w*font->image.h);
492         used_pixels = (unsigned *)malloc(font->image.w*sizeof(unsigned));
493         memset(used_pixels, 0, font->image.w*sizeof(unsigned));
494         used_glyphs = (char *)malloc(font->n_glyphs);
495         memset(used_glyphs, 0, font->n_glyphs);
496
497         for(cy=0; cy<font->image.h;)
498         {
499                 unsigned w;
500                 unsigned x, y;
501                 Glyph    *glyph = NULL;
502                 unsigned best_score = 0;
503                 unsigned target_h = 0;
504
505                 /* Find the leftmost free pixel on this row.    Also record the lowest extent of glyphs
506                 to the left of the free position. */
507                 for(; (cx<font->image.w && used_pixels[cx]>cy); ++cx)
508                         if(used_pixels[cx]-cy-1>target_h)
509                                 target_h = used_pixels[cx]-cy-1;
510
511                 if(cx>=font->image.w)
512                 {
513                         cx = 0;
514                         ++cy;
515                         continue;
516                 }
517
518                 /* Count the free pixel at this position. */
519                 for(w=0; (cx+w<font->image.w && used_pixels[cx+w]<=cy); ++w) ;
520
521                 /* Find a suitable glyph to put here. */
522                 for(i=0; i<font->n_glyphs; ++i)
523                 {
524                         Glyph *g;
525
526                         g = &font->glyphs[i];
527                         if(!used_glyphs[i] && g->image.w<=w)
528                         {
529                                 unsigned score;
530
531                                 /* Prefer glyphs that would reach exactly as low as the ones left
532                                 of here.  This aims to create a straight edge at the bottom for
533                                 lining up further glyphs. */
534                                 score = g->image.h+1;
535                                 if(g->image.h==target_h)
536                                         score *= g->image.w;
537                                 else
538                                         score += g->image.w;
539
540                                 if(score>best_score)
541                                 {
542                                         glyph = g;
543                                         best_score = score;
544                                 }
545                         }
546                 }
547
548                 if(!glyph)
549                 {
550                         cx += w;
551                         continue;
552                 }
553
554                 used_glyphs[glyph-font->glyphs] = 1;
555                 glyph->x = cx;
556                 glyph->y = cy;
557
558                 for(y=0; y<glyph->image.h; ++y) for(x=0; x<glyph->image.w; ++x)
559                 {
560                         if(cx+x<0 || cx+x>=font->image.w || cy+y<0 || cy+y>=font->image.h)
561                                 continue;
562                         font->image.data[cx+x+(cy+y)*font->image.w] = 255-glyph->image.data[x+y*glyph->image.w];
563                 }
564                 for(x=0; x<glyph->image.w+2; ++x)
565                 {
566                         if(cx+x<1 || cx+x>font->image.w)
567                                 continue;
568                         if(used_pixels[cx+x-1]<cy+glyph->image.h+1)
569                                 used_pixels[cx+x-1] = cy+glyph->image.h+1;
570                 }
571
572                 if(cy+glyph->image.h>used_h)
573                         used_h = cy+glyph->image.h;
574         }
575
576         /* Trim the image to the actually used size, in case the original estimate
577         was too pessimistic. */
578         font->image.h = round_to_pot(used_h);
579 }
580
581 int save_defs(const char *fn, const Font *font)
582 {
583         FILE     *out;
584         unsigned i;
585
586         out = fopen(fn, "w");
587         if(!out)
588         {
589                 fprintf(stderr, "Couldn't open %s\n",fn);
590                 return -1;
591         }
592
593         fprintf(out, "# Image/font info:\n");
594         fprintf(out, "# width height size ascent descent\n");
595         fprintf(out, "%d %d %d %d %d\n", font->image.w, font->image.h, font->size, font->ascent, font->descent);
596         fprintf(out, "\n# Glyph info:\n");
597         fprintf(out, "# code x y width height offset_x offset_y advance\n");
598         for(i=0; i<font->n_glyphs; ++i)
599         {
600                 const Glyph *g = &font->glyphs[i];
601                 fprintf(out, "%u %u %u %u %u %d %d %d\n", g->code, g->x, g->y, g->image.w, g->image.h, g->offset_x, g->offset_y, g->advance);
602         }
603
604         fclose(out);
605
606         return 0;
607 }
608
609 int save_png(const char *fn, const Image *image, char alpha)
610 {
611         FILE       *out;
612         png_struct *pngs;
613         png_info   *pngi;
614         png_byte   *rows[image->h];
615         int        i;
616         png_byte   *data2;
617         int        color;
618
619         if(!strcmp(fn, "-"))
620                 out = stdout;
621         else
622         {
623                 out = fopen(fn, "wb");
624                 if(!out)
625                 {
626                         fprintf(stderr, "Couldn't open %s\n",fn);
627                         return -1;
628                 }
629         }
630
631         pngs = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
632         if(!pngs)
633         {
634                 fprintf(stderr, "Error writing PNG file\n");
635                 return -1;
636         }
637         pngi = png_create_info_struct(pngs);
638         if(!pngi)
639         {
640                 png_destroy_write_struct(&pngs, NULL);
641                 fprintf(stderr, "Error writing PNG file\n");
642                 return -1;
643         }
644
645         png_init_io(pngs, out);
646         if(alpha)
647         {
648                 data2 = (png_byte *)malloc(image->w*image->h*2);
649                 for(i=0; i<image->w*image->h; ++i)
650                 {
651                         data2[i*2] = 255;
652                         data2[i*2+1] = 255-image->data[i];
653                 }
654                 for(i=0; i<image->h; ++i)
655                         rows[i] = (png_byte *)(data2+i*image->w*2);
656                 color = PNG_COLOR_TYPE_GRAY_ALPHA;
657         }
658         else
659         {
660                 for(i=0; i<image->h; ++i)
661                         rows[i] = (png_byte *)(image->data+i*image->w);
662                 color = PNG_COLOR_TYPE_GRAY;
663         }
664         png_set_IHDR(pngs, pngi, image->w, image->h, 8, color, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
665         png_set_rows(pngs, pngi, rows);
666         png_write_png(pngs, pngi, PNG_TRANSFORM_IDENTITY, NULL);
667         png_destroy_write_struct(&pngs, &pngi);
668         if(alpha)
669                 free(data2);
670
671         if(verbose)
672                 printf("Saved %dx%d PNG image to %s\n", image->w, image->h, fn);
673
674         fclose(out);
675
676         return 0;
677 }