]> git.tdb.fi Git - ttf2png.git/blob - ttf2png.c
Version 0.2
[ttf2png.git] / ttf2png.c
1 /*
2 ttf2png - True Type Font to PNG converter
3 Copyright (c) 2004 Mikkosoft Productions
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 <unistd.h>
22 #include <getopt.h>
23 #include <png.h>
24 #include <ft2build.h>
25 #include FT_FREETYPE_H
26
27 unsigned round_to_pot(unsigned);
28 void usage();
29 int save_png(char *, char *, int, int);
30
31 char verbose=0;
32 char alpha=0;
33
34 int main(int argc, char **argv)
35 {
36         FT_Library freetype;
37         FT_Face    face;
38         int  err;
39         int  o;
40         int  begin=0;
41         int  end=255;
42         char *fn;
43         int  size=10;
44         int  cpl=16;
45         int  w,h;
46         int  i;
47         char *data;
48         int  ch,cw;
49         int  cell=16;
50         int  ascent;
51         char *out="font.png";
52         char autohinter=0;
53         int  count;
54         char *def_fn=NULL;
55         char seq=0;
56         FILE *def=NULL;
57
58         if(argc<2)
59         {
60                 usage();
61                 return 1;
62         }
63
64         while((o=getopt(argc, argv, "r:s:l:c:o:atvh?ed:"))!=-1)
65         {
66                 char *ptr;
67                 int  temp;
68                 switch(o)
69                 {
70                 case 'r':
71                         if(!strcmp(optarg, "all"))
72                         {
73                                 begin=0;
74                                 end=0x110000;
75                         }
76                         else
77                         {
78                                 if(!isdigit(optarg[0]))
79                                         temp=-1;
80                                 else
81                                 {
82                                         temp=strtol(optarg, &ptr, 0);
83                                         if(ptr[0]!=',' || !isdigit(ptr[1]))
84                                                 temp=-1;
85                                 }
86                                 if(temp<0)
87                                         printf("Not a valid range: %s\n", optarg);
88                                 else
89                                 {
90                                         begin=temp;
91                                         end=strtol(ptr+1, NULL, 0);
92                                 }
93                         }
94                         break;
95                 case 's':
96                         size=strtol(optarg, NULL, 0);
97                         break;
98                 case 'l':
99                         cpl=strtol(optarg, NULL, 0);
100                         break;
101                 case 'c':
102                         cell=strtol(optarg, NULL, 0);
103                         break;
104                 case 'o':
105                         out=optarg;
106                         break;
107                 case 'a':
108                         autohinter=1;
109                         break;
110                 case 't':
111                         alpha=1;
112                         break;
113                 case 'v':
114                         ++verbose;
115                         break;
116                 case 'h':
117                 case '?':
118                         usage();
119                         return 0;
120                 case 'e':
121                         seq=1;
122                         break;
123                 case 'd':
124                         def_fn=optarg;
125                         break;
126                 }
127         }
128         if(!strcmp(out,"-"))
129                 verbose=0;
130
131         if(optind!=argc-1)
132         {
133                 usage();
134                 return 1;
135         }
136         
137         fn=argv[optind];
138
139         err=FT_Init_FreeType(&freetype);
140         if(err)
141         {
142                 fprintf(stderr, "Couldn't initialize FreeType library\n");
143                 return 1;
144         }
145
146         err=FT_New_Face(freetype, fn, 0, &face);
147         if(err)
148         {
149                 fprintf(stderr, "Couldn't load font file\n");
150                 if(err==FT_Err_Unknown_File_Format)
151                         fprintf(stderr, "Unknown file format\n");
152                 return 1;
153         }
154
155         if(verbose)
156         {
157                 const char *name=FT_Get_Postscript_Name(face);
158                 printf("Font name: %s\n", name);
159                 printf("Glyphs: %ld\n", face->num_glyphs);
160         }
161
162         err=FT_Set_Pixel_Sizes(face, 0, size);
163         if(err)
164         {
165                 fprintf(stderr, "Couldn't set size\n");
166                 return 1;
167         }
168         ascent=(face->bbox.yMax*face->size->metrics.x_scale)>>16;
169         ascent=(ascent+63)/64;
170         ch=((face->bbox.yMax-face->bbox.yMin)*face->size->metrics.y_scale)>>16;
171         ch=(ch+63)/64;
172         cw=((face->bbox.xMax-face->bbox.xMin)*face->size->metrics.x_scale)>>16;
173         cw=(cw+63)/64;
174         if(verbose)
175         {
176                 printf("Ascent %d\n",ascent);
177                 printf("Descent %ld\n",(((face->bbox.yMin*face->size->metrics.y_scale)>>16)+63)/64);
178                 printf("Max height %d\n",ch);
179                 printf("Max width %d\n",cw);
180         }
181         if(verbose>=1 && (ch>cell || cw>cell)) fprintf(stderr,"Warning: character size exceeds cell size\n");
182
183         w=round_to_pot(cpl*cell);
184         if(seq)
185                 h=(face->num_glyphs+cpl-1)/cpl*cell;
186         else
187                 h=(end-begin+cpl-1)/cpl*cell;
188         h=round_to_pot(h);
189         
190         data=(char *)malloc(w*h);
191         memset(data, 255, w*h);
192
193         if(def_fn)
194                 def=fopen(def_fn, "w");
195
196         if(def)
197                 fprintf(def, "%d %d %d\n", w, h, size);
198
199         count=0;
200         for(i=begin; i<=end; ++i)
201         {
202                 int       glyph=FT_Get_Char_Index(face,i);
203                 FT_Bitmap *bmp=&face->glyph->bitmap;
204                 int       x,y;
205                 int       cx,cy;
206                 int       flags=0;
207                 int       dy;
208
209                 if(!glyph) continue;
210                 
211                 if(seq)
212                 {
213                         cx=(count%cpl)*cell;
214                         cy=(count/cpl)*cell;
215                 }
216                 else
217                 {
218                         cx=(i%cpl)*cell;
219                         cy=(i/cpl)*cell;
220                 }
221
222                 if(autohinter)
223                         flags|=FT_LOAD_FORCE_AUTOHINT;
224                 FT_Load_Glyph(face, glyph, flags);
225                 FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
226                 if(cell>bmp->width)
227                         cx+=(cell-bmp->width)/2;
228                 dy=ascent-face->glyph->bitmap_top;
229                 cy+=dy;
230                 
231                 if(verbose>=2)
232                 {
233                         printf("  Char %d: glyph %d, size %dx%d\n", i, glyph, bmp->width, bmp->rows);
234                         if(bmp->width>cell || dy+bmp->rows>cell || dy<0) printf("  Warning: Character %d does not fit in cell\n", i);
235                 }
236                 
237                 if(bmp->pitch<0)
238                 {
239                         fprintf(stderr, "Warning: Character %d not rendered (can't handle reversed bitmaps)\n", i);
240                         continue;
241                 }
242                 
243                 for(y=0; y<bmp->rows; ++y) for(x=0; x<bmp->width; ++x)
244                 {
245                         if(cx+x<0 || cx+x>=w || cy+y<0 || cy+y>=h) continue;
246                         data[cx+x+(cy+y)*w]=255-bmp->buffer[x+y*bmp->pitch];
247                 }
248
249                 if(def)
250                         fprintf(def, "%d %d %d %d %d %d %d\n",i, cx, cy, bmp->width, bmp->rows, face->glyph->bitmap_top-bmp->rows, (int)(face->glyph->advance.x+32)/64);
251
252                 ++count;
253         }
254
255         if(def)
256                 fclose(def);
257
258         save_png(out, data, w, h);
259
260         if(verbose) printf("Converted %d glyphs\n",count);
261
262         return 0;
263 }
264
265 unsigned round_to_pot(unsigned n)
266 {
267         n-=1;
268         n|=n>>1;
269         n|=n>>2;
270         n|=n>>4;
271         n|=n>>8;
272         n|=n>>16;
273
274         return n+1;
275 }
276         
277 void usage()
278 {
279         printf("ttf2png - True Type Font to PNG converter\n"
280                 "Copyright (c) 2004 Mikkosoft Productions\n"
281                 "Distributed under the GNU General Public License\n\n"
282                 "Usage: ttf2png [options] <TTF file>\n\n"
283                 "Accepted options (default values in [brackets])\n"
284                 "  -r  Range of characters to convert in the format low,high [0,255]\n"
285                 "  -s  Font size to use, in pixels [10]\n"
286                 "  -l  Number of characters to put in one line [16]\n"
287                 "  -c  Character cell size, in pixels [16]\n"
288                 "  -o  Output file name (or - for stdout) [font.png]\n"
289                 "  -a  Force autohinter\n"
290                 "  -t  Render font to alpha channel\n"
291                 "  -v  Increase the level of verbosity\n"
292                 "  -e  Use cells in sequence, rather than by code\n"
293                 "  -d  Write a definition to the given file\n"
294                 "  -h  Print this message\n");
295 }
296
297 int save_png(char *fn, char *data, int w, int h)
298 {
299         FILE       *out;
300         png_struct *pngs;
301         png_info   *pngi;
302         png_byte   *rows[h];
303         int        i;
304         png_byte   *data2;
305         int        color;
306
307         if(!strcmp(fn, "-"))
308                 out=stdout;
309         else
310         {
311                 out=fopen(fn, "wb");
312                 if(!out)
313                 {
314                         fprintf(stderr, "Couldn't open output file\n");
315                         return -1;
316                 }
317         }
318
319         pngs=png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
320         if(!pngs)
321         {
322                 fprintf(stderr, "Error writing PNG file\n");
323                 return -1;
324         }
325         pngi=png_create_info_struct(pngs);
326         if(!pngi)
327         {
328                 png_destroy_write_struct(&pngs, NULL);
329                 fprintf(stderr, "Error writing PNG file\n");
330                 return -1;
331         }
332
333         png_init_io(pngs, out);
334         if(alpha)
335         {
336                 data2=(png_byte *)malloc(w*h*2);
337                 for(i=0; i<w*h; ++i)
338                 {
339                         data2[i*2]=255;
340                         data2[i*2+1]=255-data[i];
341                 }
342                 for(i=0; i<h; ++i)
343                         rows[i]=(png_byte *)(data2+i*w*2);
344                 color=PNG_COLOR_TYPE_GRAY_ALPHA;
345         }
346         else
347         {
348                 for(i=0; i<h; ++i)
349                         rows[i]=(png_byte *)(data+i*w);
350                 color=PNG_COLOR_TYPE_GRAY;
351         }
352         png_set_IHDR(pngs, pngi, w, h, 8, color, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
353         png_set_rows(pngs, pngi, rows);
354         png_write_png(pngs, pngi, PNG_TRANSFORM_IDENTITY, NULL);
355         png_destroy_write_struct(&pngs, &pngi);
356         if(alpha) free(data2);
357
358         if(verbose) printf("Saved %dx%d PNG image to %s\n", w, h, fn);
359
360         return 0;
361 }