]> git.tdb.fi Git - ttf2png.git/blob - ttf2png.c
Improve type usage
[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 typedef int bool;
65
66 void usage(void);
67 int convert_numeric_option(char, int);
68 void convert_code_point_range(char, unsigned *, unsigned *);
69 void convert_size(char, unsigned *, unsigned *);
70 unsigned round_to_pot(unsigned);
71 void *alloc_image_data(size_t, size_t);
72 int init_font(Font *, FT_Face, unsigned, unsigned, bool);
73 int render_grid(Font *, unsigned, unsigned, unsigned, bool);
74 int render_packed(Font *, unsigned, unsigned);
75 int save_defs(const char *, const Font *);
76 int save_png(const char *, const Image *, char);
77
78 char verbose = 0;
79
80 int main(int argc, char **argv)
81 {
82         char *fn;
83         unsigned begin = 0;
84         unsigned end = 255;
85         unsigned size = 10;
86         unsigned cpl = 0;
87         unsigned cellw = 0;
88         unsigned cellh = 0;
89         bool autohinter = 0;
90         bool seq = 0;
91         bool alpha = 0;
92         bool invert = 0;
93         bool pack = 0;
94         unsigned margin = 0;
95         unsigned padding = 1;
96
97         FT_Library freetype;
98         FT_Face face;
99
100         int err;
101         int i;
102
103         char *out_fn = "font.png";
104         char *def_fn = NULL;
105
106         Font font;
107
108         if(argc<2)
109         {
110                 usage();
111                 return 1;
112         }
113
114         while((i = getopt(argc, argv, "r:s:l:c:o:atvh?ed:pim:n:")) != -1)
115         {
116                 switch(i)
117                 {
118                 case 'r':
119                         convert_code_point_range('r', &begin, &end);
120                         break;
121                 case 's':
122                         size = convert_numeric_option('s', 1);
123                         break;
124                 case 'l':
125                         cpl = convert_numeric_option('l', 1);
126                         break;
127                 case 'c':
128                         convert_size('c', &cellw, &cellh);
129                         break;
130                 case 'o':
131                         out_fn = optarg;
132                         break;
133                 case 'a':
134                         autohinter = 1;
135                         break;
136                 case 't':
137                         alpha = 1;
138                         break;
139                 case 'v':
140                         ++verbose;
141                         break;
142                 case 'h':
143                 case '?':
144                         usage();
145                         return 0;
146                 case 'e':
147                         seq = 1;
148                         break;
149                 case 'd':
150                         def_fn = optarg;
151                         break;
152                 case 'p':
153                         pack = 1;
154                         break;
155                 case 'i':
156                         invert = 1;
157                         break;
158                 case 'm':
159                         margin = convert_numeric_option('m', 0);
160                         break;
161                 case 'n':
162                         padding = convert_numeric_option('n', 0);
163                         break;
164                 }
165         }
166         if(!strcmp(out_fn, "-"))
167                 verbose = 0;
168
169         if(optind!=argc-1)
170         {
171                 usage();
172                 return 1;
173         }
174
175         fn = argv[optind];
176
177         err = FT_Init_FreeType(&freetype);
178         if(err)
179         {
180                 fprintf(stderr, "Couldn't initialize FreeType library\n");
181                 return 1;
182         }
183
184         err = FT_New_Face(freetype, fn, 0, &face);
185         if(err)
186         {
187                 fprintf(stderr, "Couldn't load font file\n");
188                 if(err==FT_Err_Unknown_File_Format)
189                         fprintf(stderr, "Unknown file format\n");
190                 return 1;
191         }
192
193         if(verbose)
194         {
195                 const char *name = FT_Get_Postscript_Name(face);
196                 printf("Font name: %s\n", name);
197                 printf("Glyphs:    %ld\n", face->num_glyphs);
198         }
199
200         err = FT_Set_Pixel_Sizes(face, 0, size);
201         if(err)
202         {
203                 fprintf(stderr, "Couldn't set size\n");
204                 return 1;
205         }
206
207         font.size = size;
208         err = init_font(&font, face, begin, end, autohinter);
209         if(err)
210                 return 1;
211
212         if(pack)
213                 err = render_packed(&font, margin, padding);
214         else
215                 err = render_grid(&font, cellw, cellh, cpl, seq);
216         if(err)
217                 return 1;
218
219         if(invert)
220         {
221                 for(i=0; (unsigned)i<font.image.w*font.image.h; ++i)
222                         font.image.data[i] = 255-font.image.data[i];
223         }
224         err = save_png(out_fn, &font.image, alpha);
225         if(err)
226                 return 1;
227
228         if(def_fn)
229                 save_defs(def_fn, &font);
230
231         for(i=0; (unsigned)i<font.n_glyphs; ++i)
232                 free(font.glyphs[i].image.data);
233         free(font.glyphs);
234         free(font.kerning);
235         free(font.image.data);
236
237         FT_Done_Face(face);
238         FT_Done_FreeType(freetype);
239
240         return 0;
241 }
242
243 void usage(void)
244 {
245         printf("ttf2png 1.1 - True Type Font to PNG converter\n"
246                 "Copyright (c) 2004-2018  Mikko Rasa, Mikkosoft Productions\n"
247                 "Distributed under the GNU General Public License\n\n");
248
249         printf("Usage: ttf2png [options] <TTF file>\n\n");
250
251         printf("Accepted options (default values in [brackets])\n"
252                 "  -r  Range of code points to convert [0,255]\n"
253                 "  -s  Font size to use, in pixels [10]\n"
254                 "  -l  Number of glyphs to put in one line [auto]\n"
255                 "  -c  Glyph cell size, in pixels (grid mode only) [auto]\n"
256                 "  -o  Output file name (or - for stdout) [font.png]\n");
257         printf("  -a  Force autohinter\n"
258                 "  -t  Render glyphs to alpha channel\n"
259                 "  -i  Invert colors of the glyphs\n"
260                 "  -v  Increase the level of verbosity\n"
261                 "  -e  Use cells in sequence, without gaps (grid mode only)\n"
262                 "  -p  Pack the glyphs tightly instead of in a grid\n"
263                 "  -m  Margin around image edges (packed mode only) [0]\n"
264                 "  -n  Padding between glyphs (packed mode only) [1]\n"
265                 "  -d  File name for writing glyph definitions\n"
266                 "  -h  Print this message\n");
267 }
268
269 int convert_numeric_option(char opt, int min_value)
270 {
271         int value;
272         char *ptr;
273
274         value = strtol(optarg, &ptr, 0);
275         if(value<min_value || *ptr)
276         {
277                 printf("Invalid option argument in -%c %s\n", opt, optarg);
278                 exit(1);
279         }
280
281         return value;
282 }
283
284 void convert_code_point_range(char opt, unsigned *begin, unsigned *end)
285 {
286         int value;
287         char *ptr;
288
289         if(!strcmp(optarg, "all"))
290         {
291                 *begin = 0;
292                 *end = 0x10FFFF;
293                 return;
294         }
295
296         value = strtol(optarg, &ptr, 0);
297         if(value>0 && *ptr==',')
298         {
299                 *begin = value;
300                 value = strtol(ptr+1, &ptr, 0);
301                 if(value>0 && !*ptr)
302                 {
303                         *end = value;
304                         return;
305                 }
306         }
307
308         printf("Invalid option argument in -%c %s\n", opt, optarg);
309         exit(1);
310 }
311
312 void convert_size(char opt, unsigned *width, unsigned *height)
313 {
314         int value;
315         char *ptr;
316
317         if(!strcmp(optarg, "auto"))
318         {
319                 *width = 0;
320                 *height = 0;
321                 return;
322         }
323         else if(!strcmp(optarg, "autorect"))
324         {
325                 *width = 0;
326                 *height = 1;
327                 return;
328         }
329
330         value = strtol(optarg, &ptr, 0);
331         if(value>0)
332         {
333                 *width = value;
334                 if(*ptr=='x')
335                 {
336                         value = strtol(ptr+1, &ptr, 0);
337                         if(value>0 && !*ptr)
338                         {
339                                 *height = value;
340                                 return;
341                         }
342                 }
343                 else if(!*ptr)
344                 {
345                         *height = *width;
346                         return;
347                 }
348         }
349
350         printf("Invalid option argument in -%c %s\n", opt, optarg);
351         exit(1);
352 }
353
354 unsigned round_to_pot(unsigned n)
355 {
356         n -= 1;
357         n |= n>>1;
358         n |= n>>2;
359         n |= n>>4;
360         n |= n>>8;
361         n |= n>>16;
362
363         return n+1;
364 }
365
366 void *alloc_image_data(size_t a, size_t b)
367 {
368         void *ptr;
369
370         /* Carry out the multiplication manually so we can check for overflow. */
371         while(b>1)
372         {
373                 size_t c = a;
374                 a *= 2;
375                 if(b&1)
376                         a += c;
377                 if(a<c)
378                 {
379                         fprintf(stderr, "Cannot allocate %lu kbytes of memory for image\n", (unsigned long)(c/1024*b));
380                         return NULL;
381                 }
382                 b /= 2;
383         }
384         ptr = malloc(a);
385         if(!ptr)
386                 fprintf(stderr, "Cannot allocate %lu kbytes of memory for image\n", (unsigned long)(a/1024*b));
387         return ptr;
388 }
389
390 int init_font(Font *font, FT_Face face, unsigned first, unsigned last, bool autohinter)
391 {
392         unsigned i, j;
393         unsigned size = 0;
394
395         font->ascent = (face->size->metrics.ascender+63)>>6;
396         font->descent = (face->size->metrics.descender+63)>>6;
397
398         if(verbose>=1)
399         {
400                 printf("Ascent:    %d\n", font->ascent);
401                 printf("Descent:   %d\n", font->descent);
402         }
403
404         font->n_glyphs = 0;
405         font->glyphs = NULL;
406         for(i=first; i<=last; ++i)
407         {
408                 unsigned n;
409                 FT_Bitmap *bmp = &face->glyph->bitmap;
410                 unsigned x, y;
411                 int flags = 0;
412                 Glyph *glyph;
413
414                 n = FT_Get_Char_Index(face, i);
415                 if(!n)
416                         continue;
417
418                 if(autohinter)
419                         flags |= FT_LOAD_FORCE_AUTOHINT;
420                 FT_Load_Glyph(face, n, flags);
421                 FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
422
423                 if(verbose>=2)
424                         printf("  Char %u: glyph %u, size %dx%d\n", i, n, bmp->width, bmp->rows);
425
426                 if(bmp->pixel_mode!=FT_PIXEL_MODE_GRAY)
427                 {
428                         fprintf(stderr, "Warning: Glyph %u skipped, not grayscale\n", n);
429                         continue;
430                 }
431
432                 if(font->n_glyphs>=size)
433                 {
434                         size += 16;
435                         font->glyphs = (Glyph *)realloc(font->glyphs, size*sizeof(Glyph));
436                 }
437
438                 glyph = &font->glyphs[font->n_glyphs++];
439                 glyph->index = n;
440                 glyph->code = i;
441                 glyph->image.w = bmp->width;
442                 glyph->image.h = bmp->rows;
443                 glyph->image.data = (char *)malloc(bmp->width*bmp->rows);
444                 if(!glyph->image.data)
445                 {
446                         fprintf(stderr, "Cannot allocate %d bytes of memory for glyph\n", bmp->width*bmp->rows);
447                         return -1;
448                 }
449                 glyph->offset_x = face->glyph->bitmap_left;
450                 glyph->offset_y = face->glyph->bitmap_top-bmp->rows;
451                 glyph->advance = (int)(face->glyph->advance.x+32)/64;
452
453                 /* Copy the glyph image since FreeType uses a global buffer, which would
454                 be overwritten by the next glyph.  Negative pitch means the scanlines
455                 start from the bottom. */
456                 if(bmp->pitch<0)
457                 {
458                         for(y=0; y<bmp->rows; ++y) for(x=0; x<bmp->width; ++x)
459                                 glyph->image.data[x+(glyph->image.h-1-y)*glyph->image.w] = bmp->buffer[x-y*bmp->pitch];
460                 }
461                 else
462                 {
463                         for(y=0; y<bmp->rows; ++y) for(x=0; x<bmp->width; ++x)
464                                 glyph->image.data[x+y*glyph->image.w] = bmp->buffer[x+y*bmp->pitch];
465                 }
466         }
467
468         if(verbose>=1)
469                 printf("Loaded %u glyphs\n", font->n_glyphs);
470
471         size = 0;
472         font->n_kerning = 0;
473         font->kerning = NULL;
474         for(i=0; i<font->n_glyphs; ++i) for(j=0; j<font->n_glyphs; ++j)
475                 if(j!=i)
476                 {
477                         FT_Vector kerning;
478                         FT_Get_Kerning(face, font->glyphs[i].index, font->glyphs[j].index, FT_KERNING_DEFAULT, &kerning);
479
480                         /* FreeType documentation says that vertical kerning is practically
481                         never used, so we ignore it. */
482                         if(kerning.x)
483                         {
484                                 Kerning *kern;
485
486                                 if(font->n_kerning>=size)
487                                 {
488                                         size += 16;
489                                         font->kerning = (Kerning *)realloc(font->kerning, size*sizeof(Kerning));
490                                 }
491
492                                 kern = &font->kerning[font->n_kerning++];
493                                 kern->left_code = font->glyphs[i].code;
494                                 kern->right_code = font->glyphs[j].code;
495                                 kern->distance = kerning.x/64;
496                         }
497                 }
498
499         if(verbose>=1)
500                 printf("Loaded %d kerning pairs\n", font->n_kerning);
501
502         return 0;
503 }
504
505 int render_grid(Font *font, unsigned cellw, unsigned cellh, unsigned cpl, bool seq)
506 {
507         unsigned i;
508         int top = 0, bot = 0;
509         unsigned first, last;
510         unsigned maxw = 0, maxh = 0;
511
512         /* Find extremes of the glyph images. */
513         for(i=0; i<font->n_glyphs; ++i)
514         {
515                 int y;
516
517                 y = font->glyphs[i].offset_y+font->glyphs[i].image.h;
518                 if(y>top)
519                         top = y;
520                 if(font->glyphs[i].offset_y<bot)
521                         bot = font->glyphs[i].offset_y;
522                 if(font->glyphs[i].image.w>maxw)
523                         maxw = font->glyphs[i].image.w;
524                 if(font->glyphs[i].image.h>maxh)
525                         maxh = font->glyphs[i].image.h;
526         }
527
528         if(cellw==0)
529         {
530                 /* Establish a large enough cell to hold all glyphs in the range. */
531                 int square = (cellh==cellw);
532                 cellw = maxw;
533                 cellh = top-bot;
534                 if(square)
535                 {
536                         if(cellh>cellw)
537                                 cellw = cellh;
538                         else
539                                 cellh = cellw;
540                 }
541         }
542
543         if(verbose>=1)
544         {
545                 printf("Max size:  %u x %u\n", maxw, maxh);
546                 printf("Y range:   [%d %d]\n", bot, top);
547                 printf("Cell size: %u x %u\n", cellw, cellh);
548                 if(maxw>cellw || (unsigned)(top-bot)>cellh)
549                         fprintf(stderr, "Warning: character size exceeds cell size\n");
550         }
551
552         if(cpl==0)
553         {
554                 /* Determine number of characters per line, trying to fit all the glyphs
555                 in a square image. */
556                 for(i=1;; i<<=1)
557                 {
558                         cpl = i/cellw;
559                         if(cpl>0 && font->n_glyphs/cpl*cellh<=cpl*cellw)
560                                 break;
561                 }
562         }
563
564         first = font->glyphs[0].code;
565         if(!seq)
566                 first -= first%cpl;
567         last = font->glyphs[font->n_glyphs-1].code;
568
569         font->image.w = round_to_pot(cpl*cellw);
570         if(seq)
571                 font->image.h = round_to_pot((font->n_glyphs+cpl-1)/cpl*cellh);
572         else
573                 font->image.h = round_to_pot((last-first+cpl)/cpl*cellh);
574
575         font->image.data = (char *)alloc_image_data(font->image.w, font->image.h);
576         if(!font->image.data)
577                 return -1;
578         memset(font->image.data, 255, font->image.w*font->image.h);
579
580         for(i=0; i<font->n_glyphs; ++i)
581         {
582                 Glyph *glyph;
583                 unsigned ci, cx, cy;
584                 unsigned x, y;
585
586                 glyph = &font->glyphs[i];
587
588                 if(seq)
589                         ci = i;
590                 else
591                         ci = glyph->code-first;
592
593                 cx = (ci%cpl)*cellw;
594                 cy = (ci/cpl)*cellh;
595
596                 if(cellw>glyph->image.w)
597                         cx += (cellw-glyph->image.w)/2;
598                 cy += top-glyph->offset_y-glyph->image.h;
599
600                 glyph->x = cx;
601                 glyph->y = cy;
602
603                 for(y=0; y<glyph->image.h; ++y) for(x=0; x<glyph->image.w; ++x)
604                 {
605                         if(cx+x>=font->image.w || cy+y>=font->image.h)
606                                 continue;
607                         font->image.data[cx+x+(cy+y)*font->image.w] = 255-glyph->image.data[x+y*glyph->image.w];
608                 }
609         }
610
611         return 0;
612 }
613
614 int render_packed(Font *font, unsigned margin, unsigned padding)
615 {
616         unsigned i;
617         size_t area = 0;
618         char *used_glyphs;
619         unsigned *used_pixels;
620         unsigned cx = margin, cy;
621         unsigned used_h = 0;
622
623         /* Compute the total area occupied by glyphs and padding. */
624         for(i=0; i<font->n_glyphs; ++i)
625         {
626                 size_t a = area+(font->glyphs[i].image.w+padding)*(font->glyphs[i].image.h+padding);
627                 if(a<area)
628                 {
629                         fprintf(stderr, "Overflow in counting total glyph area\n");
630                         return -1;
631                 }
632                 area = a;
633         }
634
635         /* Find an image size that's no higher than wide, allowing for some
636         imperfections in the packing. */
637         for(font->image.w=1;; font->image.w<<=1)
638         {
639                 if(font->image.w<=margin*2)
640                         continue;
641                 font->image.h = (area*5/4)/(font->image.w-margin*2)+margin*2;
642                 if(font->image.h<=font->image.w)
643                         break;
644         }
645         font->image.h = round_to_pot(font->image.h);
646
647         /* Allocate arrays for storing the image and keeping track of used pixels and
648         glyphs.  Since glyphs are rectangular and the image is filled starting from
649         the top, it's enough to track the number of used pixels at the top of each
650         column. */
651         font->image.data = (char *)alloc_image_data(font->image.w, font->image.h);
652         if(!font->image.data)
653                 return -1;
654         memset(font->image.data, 255, font->image.w*font->image.h);
655         used_pixels = (unsigned *)malloc(font->image.w*sizeof(unsigned));
656         memset(used_pixels, 0, font->image.w*sizeof(unsigned));
657         used_glyphs = (char *)malloc(font->n_glyphs);
658         memset(used_glyphs, 0, font->n_glyphs);
659
660         for(cy=margin; cy+margin<font->image.h;)
661         {
662                 unsigned w;
663                 unsigned x, y;
664                 Glyph *glyph = NULL;
665                 unsigned best_score = 0;
666                 unsigned target_h = 0;
667
668                 /* Find the leftmost free pixel on this row.  Also record the lowest
669                 extent of glyphs to the left of the free position. */
670                 for(; (cx+margin<font->image.w && used_pixels[cx]>cy); ++cx)
671                         if(used_pixels[cx]-cy-padding>target_h)
672                                 target_h = used_pixels[cx]-cy-padding;
673
674                 if(cx+margin>=font->image.w)
675                 {
676                         cx = margin;
677                         ++cy;
678                         continue;
679                 }
680
681                 /* Count the free pixel at this position. */
682                 for(w=0; (cx+w+margin<font->image.w && used_pixels[cx+w]<=cy); ++w) ;
683
684                 /* Find a suitable glyph to put here. */
685                 for(i=0; i<font->n_glyphs; ++i)
686                 {
687                         Glyph *g;
688
689                         g = &font->glyphs[i];
690                         if(!used_glyphs[i] && g->image.w<=w)
691                         {
692                                 unsigned score;
693
694                                 /* Prefer glyphs that would reach exactly as low as the ones left
695                                 of here.  This aims to create a straight edge at the bottom for
696                                 lining up further glyphs. */
697                                 score = g->image.h+padding;
698                                 if(g->image.h==target_h)
699                                         score *= g->image.w;
700                                 else
701                                         score += g->image.w;
702
703                                 if(score>best_score)
704                                 {
705                                         glyph = g;
706                                         best_score = score;
707                                 }
708                         }
709                 }
710
711                 if(!glyph)
712                 {
713                         cx += w;
714                         continue;
715                 }
716
717                 used_glyphs[glyph-font->glyphs] = 1;
718                 glyph->x = cx;
719                 glyph->y = cy;
720
721                 for(y=0; y<glyph->image.h; ++y) for(x=0; x<glyph->image.w; ++x)
722                 {
723                         if(cx+x>=font->image.w || cy+y>=font->image.h)
724                                 continue;
725                         font->image.data[cx+x+(cy+y)*font->image.w] = 255-glyph->image.data[x+y*glyph->image.w];
726                 }
727                 for(x=0; x<glyph->image.w+2*padding; ++x)
728                 {
729                         if(cx+x<padding || cx+x>=font->image.w+padding)
730                                 continue;
731                         if(used_pixels[cx+x-padding]<cy+glyph->image.h+padding)
732                                 used_pixels[cx+x-padding] = cy+glyph->image.h+padding;
733                 }
734
735                 if(cy+glyph->image.h+margin>used_h)
736                         used_h = cy+glyph->image.h+margin;
737         }
738
739         /* Trim the image to the actually used size, in case the original estimate
740         was too pessimistic. */
741         font->image.h = round_to_pot(used_h);
742
743         free(used_glyphs);
744         free(used_pixels);
745
746         return 0;
747 }
748
749 int save_defs(const char *fn, const Font *font)
750 {
751         FILE *out;
752         unsigned i;
753
754         out = fopen(fn, "w");
755         if(!out)
756         {
757                 fprintf(stderr, "Couldn't open %s\n",fn);
758                 return -1;
759         }
760
761         fprintf(out, "# Image/font info:\n");
762         fprintf(out, "# width height size ascent descent\n");
763         fprintf(out, "font %d %d %d %d %d\n", font->image.w, font->image.h, font->size, font->ascent, font->descent);
764         fprintf(out, "\n# Glyph info:\n");
765         fprintf(out, "# code x y width height offset_x offset_y advance\n");
766         for(i=0; i<font->n_glyphs; ++i)
767         {
768                 const Glyph *g = &font->glyphs[i];
769                 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);
770         }
771         fprintf(out, "\n# Kerning info:\n");
772         fprintf(out, "# left right distance\n");
773         for(i=0; i<font->n_kerning; ++i)
774         {
775                 const Kerning *k = &font->kerning[i];
776                 fprintf(out, "kern %u %u %d\n", k->left_code, k->right_code, k->distance);
777         }
778
779         fclose(out);
780
781         return 0;
782 }
783
784 int save_png(const char *fn, const Image *image, char alpha)
785 {
786         FILE *out;
787         png_struct *pngs;
788         png_info *pngi;
789         png_byte **rows;
790         unsigned i;
791         png_byte *data2 = 0;
792         int color;
793
794         if(!strcmp(fn, "-"))
795                 out = stdout;
796         else
797         {
798                 out = fopen(fn, "wb");
799                 if(!out)
800                 {
801                         fprintf(stderr, "Couldn't open %s\n",fn);
802                         return -1;
803                 }
804         }
805
806         pngs = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
807         if(!pngs)
808         {
809                 fprintf(stderr, "Error writing PNG file\n");
810                 return -1;
811         }
812         pngi = png_create_info_struct(pngs);
813         if(!pngi)
814         {
815                 png_destroy_write_struct(&pngs, NULL);
816                 fprintf(stderr, "Error writing PNG file\n");
817                 return -1;
818         }
819
820         png_init_io(pngs, out);
821         rows = (png_byte **)malloc(image->h*sizeof(png_byte *));
822         if(alpha)
823         {
824                 data2 = (png_byte *)alloc_image_data(image->w*2, image->h);
825                 if(!data2)
826                         return -1;
827                 for(i=0; i<image->w*image->h; ++i)
828                 {
829                         data2[i*2] = 255;
830                         data2[i*2+1] = 255-image->data[i];
831                 }
832                 for(i=0; i<image->h; ++i)
833                         rows[i] = (png_byte *)(data2+i*image->w*2);
834                 color = PNG_COLOR_TYPE_GRAY_ALPHA;
835         }
836         else
837         {
838                 for(i=0; i<image->h; ++i)
839                         rows[i] = (png_byte *)(image->data+i*image->w);
840                 color = PNG_COLOR_TYPE_GRAY;
841         }
842         png_set_IHDR(pngs, pngi, image->w, image->h, 8, color, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
843         png_set_rows(pngs, pngi, rows);
844         png_write_png(pngs, pngi, PNG_TRANSFORM_IDENTITY, NULL);
845         png_destroy_write_struct(&pngs, &pngi);
846         free(rows);
847         if(alpha)
848                 free(data2);
849
850         if(verbose)
851                 printf("Saved %dx%d PNG image to %s\n", image->w, image->h, fn);
852
853         fclose(out);
854
855         return 0;
856 }