]> git.tdb.fi Git - ttf2png.git/blobdiff - ttf2png.c
Version 0.2.1
[ttf2png.git] / ttf2png.c
index 97be0f1fc6ef4934f991bfc323c5b80a02eecd93..bd8410674d8bdc67765689bed3fbc9f21aa30158 100644 (file)
--- a/ttf2png.c
+++ b/ttf2png.c
@@ -1,6 +1,6 @@
 /*
 ttf2png - True Type Font to PNG converter
-Copyright (c) 2004 Mikkosoft Productions
+Copyright (c) 2004-2006 Mikkosoft Productions
 
 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -24,31 +24,49 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 #include <ft2build.h>
 #include FT_FREETYPE_H
 
+typedef struct sGlyphDef
+{
+       int code;
+       int x,y;
+       int w,h;
+       int ascent;
+       int advance;
+} GlyphDef;
+
+unsigned round_to_pot(unsigned);
 void usage();
-int save_png(char *, char *, int, int);
+int save_defs(const char *, const GlyphDef *, int, int, int, int);
+int save_png(const char *, const char *, int, int, char);
+
+char verbose=0;
 
 int main(int argc, char **argv)
 {
-       FT_Library      freetype;
-       FT_Face         face;
-       int     err;
-       int     o;
-       int     begin=0;
-       int     end=255;
-       char    *fn;
-       int     size=10;
-       int     cpl=16;
-       int     w,h;
-       int     i;
-       char    *data;
-       int     ch,cw;
-       int     cell=16;
-       int     ascent;
-       char    *out="font.png";
-       char    verbose=0;
-       const char      *name;
-       char    autohinter=0;
-       int     count;
+       char *fn;
+       int  begin=0;
+       int  end=255;
+       int  size=10;
+       int  cpl=16;
+       int  cell=16;
+       char autohinter=0;
+       char seq=0;
+       char alpha=0;
+
+       FT_Library freetype;
+       FT_Face    face;
+       int  ch,cw;
+       int  ascent;
+
+       int  err;
+       int  i;
+       int  count;
+
+       int  w,h;
+       char *data;
+       char *out_fn="font.png";
+
+       char *def_fn=NULL;
+       GlyphDef *defs=NULL;
 
        if(argc<2)
        {
@@ -56,133 +74,236 @@ int main(int argc, char **argv)
                return 1;
        }
 
-       while((o=getopt(argc,argv,"r:s:l:c:o:avh?"))!=-1)
+       while((i=getopt(argc, argv, "r:s:l:c:o:atvh?ed:"))!=-1)
        {
-               char    *ptr;
-               int     temp;
-               switch(o)
+               char *ptr;
+               int  temp;
+               switch(i)
                {
                case 'r':
-                       if(!isdigit(optarg[0])) temp=-1;
-                       else
+                       if(!strcmp(optarg, "all"))
                        {
-                               temp=strtol(optarg,&ptr,0);
-                               if(ptr[0]!=',' || !isdigit(ptr[1])) temp=-1;
+                               begin=0;
+                               end=0x110000;
                        }
-                       if(temp<0)
-                               printf("Not a valid range: %s\n",optarg);
                        else
                        {
-                               begin=temp;
-                               end=strtol(ptr+1,NULL,0);
+                               if(!isdigit(optarg[0]))
+                                       temp=-1;
+                               else
+                               {
+                                       temp=strtol(optarg, &ptr, 0);
+                                       if(ptr[0]!=',' || !isdigit(ptr[1]))
+                                               temp=-1;
+                               }
+                               if(temp<0)
+                               {
+                                       printf("Not a valid range: %s\n", optarg);
+                                       exit(1);
+                               }
+                               else
+                               {
+                                       begin=temp;
+                                       end=strtol(ptr+1, NULL, 0);
+                               }
                        }
                        break;
                case 's':
-                       size=strtol(optarg,NULL,0);
+                       size=strtol(optarg, NULL, 0);
                        break;
                case 'l':
-                       cpl=strtol(optarg,NULL,0);
+                       cpl=strtol(optarg, NULL, 0);
                        break;
                case 'c':
-                       cell=strtol(optarg,NULL,0);
+                       cell=strtol(optarg, NULL, 0);
                        break;
                case 'o':
-                       out=optarg;
+                       out_fn=optarg;
                        break;
                case 'a':
                        autohinter=1;
                        break;
+               case 't':
+                       alpha=1;
+                       break;
                case 'v':
-                       verbose=1;
+                       ++verbose;
                        break;
                case 'h':
                case '?':
                        usage();
                        return 0;
+               case 'e':
+                       seq=1;
+                       break;
+               case 'd':
+                       def_fn=optarg;
+                       break;
                }
        }
+       if(!strcmp(out_fn, "-"))
+               verbose=0;
 
-       if(optind>=argc)
+       if(optind!=argc-1)
        {
                usage();
                return 1;
        }
+       
        fn=argv[optind];
 
        err=FT_Init_FreeType(&freetype);
        if(err)
        {
-               fprintf(stderr,"Couldn't initialize FreeType library\n");
+               fprintf(stderr, "Couldn't initialize FreeType library\n");
                return 1;
        }
 
-       err=FT_New_Face(freetype,fn,0,&face);
+       err=FT_New_Face(freetype, fn, 0, &face);
        if(err)
        {
-               fprintf(stderr,"Couldn't load font file\n");
+               fprintf(stderr, "Couldn't load font file\n");
                if(err==FT_Err_Unknown_File_Format)
-                       fprintf(stderr,"Unknown file format\n");
+                       fprintf(stderr, "Unknown file format\n");
                return 1;
        }
 
-       name=FT_Get_Postscript_Name(face);
-       if(verbose) printf("Font name: %s\n",name);
+       if(verbose)
+       {
+               const char *name=FT_Get_Postscript_Name(face);
+               printf("Font name: %s\n", name);
+               printf("Glyphs: %ld\n", face->num_glyphs);
+       }
 
-       err=FT_Set_Pixel_Sizes(face,0,size);
+       err=FT_Set_Pixel_Sizes(face, 0, size);
        if(err)
        {
-               fprintf(stderr,"Couldn't set size\n");
+               fprintf(stderr, "Couldn't set size\n");
                return 1;
        }
-       ascent=face->size->metrics.ascender/64;
-       ch=(face->size->metrics.ascender-face->size->metrics.descender)/64;
-       cw=face->size->metrics.max_advance/64;
+       ascent=(face->bbox.yMax*face->size->metrics.x_scale)>>16;
+       ascent=(ascent+63)/64;
+       ch=((face->bbox.yMax-face->bbox.yMin)*face->size->metrics.y_scale)>>16;
+       ch=(ch+63)/64;
+       cw=((face->bbox.xMax-face->bbox.xMin)*face->size->metrics.x_scale)>>16;
+       cw=(cw+63)/64;
        if(verbose)
        {
                printf("Ascent %d\n",ascent);
-               printf("Descent %ld\n",face->size->metrics.descender/64);
-               printf("Height %d\n",ch);
-               printf("Width %d\n",cw);
+               printf("Descent %ld\n",(((face->bbox.yMin*face->size->metrics.y_scale)>>16)+63)/64);
+               printf("Max height %d\n",ch);
+               printf("Max width %d\n",cw);
        }
-       if(ch>cell || cw>cell) fprintf(stderr,"ttf2png: Warning: character size exceeds cell size\n");
+       if(verbose>=1 && (ch>cell || cw>cell)) fprintf(stderr,"Warning: character size exceeds cell size\n");
 
-       w=cpl*cell;
-       h=(end-begin+cpl-1)/cpl*cell;
+       w=round_to_pot(cpl*cell);
+       if(seq && face->num_glyphs<end-begin)
+               h=(face->num_glyphs+cpl-1)/cpl*cell;
+       else
+               h=(end-begin+cpl-1)/cpl*cell;
+       h=round_to_pot(h);
+       
        data=(char *)malloc(w*h);
-       memset(data,255,w*h);
+       memset(data, 255, w*h);
+
+       if(def_fn)
+       {
+               if(face->num_glyphs<end-begin)
+                       count=face->num_glyphs;
+               else
+                       count=end-begin;
+               
+               defs=(GlyphDef *)malloc(count*sizeof(GlyphDef));
+       }
 
        count=0;
-       for(i=begin;i<=end;++i)
-       {
-               int                     glyph=FT_Get_Char_Index(face,i);
-               FT_Bitmap       *bmp=&face->glyph->bitmap;
-               int                     x,y;
-               int                     cx=(i%cpl)*cell+(cell-cw)/2;
-               int                     cy=(i/cpl)*cell;
-               int                     flags=0;
+       for(i=begin; i<=end; ++i)
+       {
+               int       glyph=FT_Get_Char_Index(face,i);
+               FT_Bitmap *bmp=&face->glyph->bitmap;
+               int       x,y;
+               int       cx,cy;
+               int       flags=0;
+               int       dy;
+
                if(!glyph) continue;
-               fflush(stdout);
-               if(autohinter) flags|=FT_LOAD_FORCE_AUTOHINT;
-               FT_Load_Glyph(face,glyph,flags);
-               FT_Render_Glyph(face->glyph,FT_RENDER_MODE_NORMAL);
-               cx+=face->glyph->bitmap_left;
-               cy+=ascent-face->glyph->bitmap_top;
-               for(y=0;y<bmp->rows;++y) for(x=0;x<bmp->width;++x)
+               
+               if(seq)
+               {
+                       cx=(count%cpl)*cell;
+                       cy=(count/cpl)*cell;
+               }
+               else
+               {
+                       cx=(i%cpl)*cell;
+                       cy=(i/cpl)*cell;
+               }
+
+               if(autohinter)
+                       flags|=FT_LOAD_FORCE_AUTOHINT;
+               FT_Load_Glyph(face, glyph, flags);
+               FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
+               if(cell>bmp->width)
+                       cx+=(cell-bmp->width)/2;
+               dy=ascent-face->glyph->bitmap_top;
+               cy+=dy;
+               
+               if(verbose>=2)
+               {
+                       printf("  Char %d: glyph %d, size %dx%d\n", i, glyph, bmp->width, bmp->rows);
+                       if(bmp->width>cell || dy+bmp->rows>cell || dy<0) printf("  Warning: Character %d does not fit in cell\n", i);
+               }
+               
+               if(bmp->pitch<0)
+               {
+                       fprintf(stderr, "Warning: Character %d not rendered (can't handle reversed bitmaps)\n", i);
+                       continue;
+               }
+               
+               for(y=0; y<bmp->rows; ++y) for(x=0; x<bmp->width; ++x)
                {
                        if(cx+x<0 || cx+x>=w || cy+y<0 || cy+y>=h) continue;
-                       if(bmp->pitch>0)
-                               data[cx+x+(cy+y)*w]=255-bmp->buffer[x+y*bmp->pitch];
+                       data[cx+x+(cy+y)*w]=255-bmp->buffer[x+y*bmp->pitch];
                }
+
+               if(def_fn)
+               {
+                       defs[count].code=i;
+                       defs[count].x=cx;
+                       defs[count].y=cy;
+                       defs[count].w=bmp->width;
+                       defs[count].h=bmp->rows;
+                       defs[count].ascent=face->glyph->bitmap_top-bmp->rows;
+                       defs[count].advance=(int)(face->glyph->advance.x+32)/64;
+               }
+
                ++count;
        }
 
-       save_png(out,data,w,h);
+       while(seq && h/2>=(count+cpl-1)/cpl*cell)
+               h/=2;
+
+       if(def_fn)
+               save_defs(def_fn, defs, count, w, h, size);
+       save_png(out_fn, data, w, h, alpha);
 
-       if(verbose) printf("Converted %d glyphs\n",count);
+       if(verbose) printf("Converted %d glyphs\n", count);
 
        return 0;
 }
 
+unsigned round_to_pot(unsigned n)
+{
+       n-=1;
+       n|=n>>1;
+       n|=n>>2;
+       n|=n>>4;
+       n|=n>>8;
+       n|=n>>16;
+
+       return n+1;
+}
+       
 void usage()
 {
        printf("ttf2png - True Type Font to PNG converter\n"
@@ -194,48 +315,101 @@ void usage()
                "  -s  Font size to use, in pixels [10]\n"
                "  -l  Number of characters to put in one line [16]\n"
                "  -c  Character cell size, in pixels [16]\n"
-               "  -o  Output file name [font.png]\n"
+               "  -o  Output file name (or - for stdout) [font.png]\n"
                "  -a  Force autohinter\n"
-               "  -v  Enable verbose mode\n"
+               "  -t  Render font to alpha channel\n"
+               "  -v  Increase the level of verbosity\n"
+               "  -e  Use cells in sequence, rather than by code\n"
+               "  -d  Write a definition to the given file\n"
                "  -h  Print this message\n");
 }
 
-int save_png(char *fn, char *data, int w, int h)
+int save_defs(const char *fn, const GlyphDef *defs, int count, int w, int h, int size)
 {
-       FILE                    *out;
-       png_struct      *pngs;
-       png_info                *pngi;
-       png_byte                *rows[h];
-       int                     i;
+       FILE *out;
+       int  i;
 
-       out=fopen(fn,"wb");
+       out=fopen(fn, "w");
        if(!out)
        {
-               fprintf(stderr,"Couldn't open output file\n");
+               fprintf(stderr, "Couldn't open %s\n",fn);
                return -1;
        }
 
-       pngs=png_create_write_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL);
+       fprintf(out, "%d %d %d\n", w, h, size);
+       for(i=0; i<count; ++i)
+       {
+               const GlyphDef *d=defs+i;
+               fprintf(out, "%d %d %d %d %d %d %d\n", d->code, d->x, d->y, d->w, d->h, d->ascent, d->advance);
+       }
+
+       fclose(out);
+
+       return 0;
+}
+
+int save_png(const char *fn, const char *data, int w, int h, char alpha)
+{
+       FILE       *out;
+       png_struct *pngs;
+       png_info   *pngi;
+       png_byte   *rows[h];
+       int        i;
+       png_byte   *data2;
+       int        color;
+
+       if(!strcmp(fn, "-"))
+               out=stdout;
+       else
+       {
+               out=fopen(fn, "wb");
+               if(!out)
+               {
+                       fprintf(stderr, "Couldn't open %s\n",fn);
+                       return -1;
+               }
+       }
+
+       pngs=png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
        if(!pngs)
        {
-               fprintf(stderr,"Error writing PNG file\n");
+               fprintf(stderr, "Error writing PNG file\n");
                return -1;
        }
        pngi=png_create_info_struct(pngs);
        if(!pngi)
        {
-               png_destroy_write_struct(&pngs,NULL);
-               fprintf(stderr,"Error writing PNG file\n");
+               png_destroy_write_struct(&pngs, NULL);
+               fprintf(stderr, "Error writing PNG file\n");
                return -1;
        }
 
-       png_init_io(pngs,out);
-       png_set_IHDR(pngs,pngi,w,h,8,PNG_COLOR_TYPE_GRAY,PNG_INTERLACE_NONE,PNG_COMPRESSION_TYPE_DEFAULT,PNG_FILTER_TYPE_DEFAULT);
-       for(i=0;i<h;++i)
-               rows[i]=(png_byte *)(data+i*w);
-       png_set_rows(pngs,pngi,rows);
-       png_write_png(pngs,pngi,PNG_TRANSFORM_IDENTITY,NULL);
-       png_destroy_write_struct(&pngs,&pngi);
+       png_init_io(pngs, out);
+       if(alpha)
+       {
+               data2=(png_byte *)malloc(w*h*2);
+               for(i=0; i<w*h; ++i)
+               {
+                       data2[i*2]=255;
+                       data2[i*2+1]=255-data[i];
+               }
+               for(i=0; i<h; ++i)
+                       rows[i]=(png_byte *)(data2+i*w*2);
+               color=PNG_COLOR_TYPE_GRAY_ALPHA;
+       }
+       else
+       {
+               for(i=0; i<h; ++i)
+                       rows[i]=(png_byte *)(data+i*w);
+               color=PNG_COLOR_TYPE_GRAY;
+       }
+       png_set_IHDR(pngs, pngi, w, h, 8, color, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+       png_set_rows(pngs, pngi, rows);
+       png_write_png(pngs, pngi, PNG_TRANSFORM_IDENTITY, NULL);
+       png_destroy_write_struct(&pngs, &pngi);
+       if(alpha) free(data2);
+
+       if(verbose) printf("Saved %dx%d PNG image to %s\n", w, h, fn);
 
        return 0;
 }