/*
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
#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)
{
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"
" -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;
}