/* bdf.c : BDF font loader */ /* original Copyright 2003 Jon Mayo -- June 1, 2003 * released to PUBLIC DOMAIN on August 25, 2006 * Last Updated: April 10, 2008 */ /**************************************************************************/ #include #include #include #include #include #include #include #include "bdf.h" #define BDF_ALIGNMENT 4 #define BDF_INVERT /* define this to store glyphs upside-down. useful for OpenGL glBitmap() */ #define BDF_DUMP_START ' ' #define BDF_DUMP_END '~' #define BDF_WARN_UNKNOWN_TAGS #define DEBUG_INFO(m, ...) fprintf(stderr, "INFO:" m, ## __VA_ARGS__) #define DEBUG_WARN(m, ...) fprintf(stderr, "WARN:" m, ## __VA_ARGS__) #define DEBUG_ERROR(m, ...) fprintf(stderr, "ERROR:" m, ## __VA_ARGS__) /* static void bindump(unsigned char x) { int i; for(i=0;i>i)&1)+'0' , stdout); } } */ /* create an empty glyph */ static struct bdf_glyph *make_glyph(int width, int height) { struct bdf_glyph *ret; unsigned pitch; /* round up to 8-bit boundries and pad to BDF_ALIGNMENT bytes */ pitch=((width+BDF_ALIGNMENT*CHAR_BIT-1)/(BDF_ALIGNMENT*CHAR_BIT))*BDF_ALIGNMENT; /* append the data onto the end of the struct and malloc the whole thing. */ ret=calloc(1,sizeof(struct bdf_glyph)+(pitch*height)); ret->rc=0; ret->width=width; ret->height=height; ret->pitch=pitch; return ret; } /* release the data assocaited with a glyph. do not call with a NULL value */ static inline void free_glyph(struct bdf_glyph *g) { if(!g->rc--) { /* when the RC would drop below 0 free it */ free(g); } } /* reference an existing glyph */ inline struct bdf_glyph *take_glyph(struct bdf_glyph *g) { g->rc++; return g; } /* determines the number to pass to a malloc/realloc for a font structure with * a particular count */ static inline int font_alloc_size(int count) { return sizeof(struct bdf_font)+count*sizeof(struct bdf_glyph*); } /* creates a new font structure. */ static struct bdf_font *make_font(int initial_size) { struct bdf_font *ret; /* malloc a struct with a table of pointers to glyphs on the end */ ret=calloc(1,font_alloc_size(initial_size)); ret->undefined='@'; ret->ascent=0; ret->descent=0; return ret; } /* stop referencing a glyph */ static void unset_font_glyph(struct bdf_font **f, unsigned id) { if(f && *f && id<(*f)->nr_glyph) { /* ignore if value is out of range */ if((*f)->glyph[id]) { free_glyph((*f)->glyph[id]); (*f)->glyph[id]=NULL; /* cease referencing it */ } } } /* expands the font to support more glyphs */ static void grow_font(struct bdf_font **f, unsigned newsize) { unsigned i; /* for making it smaller: free all the glyphs that will die */ for(i=newsize;i<(*f)->nr_glyph;i++) { unset_font_glyph(f,i); } *f=realloc(*f, font_alloc_size(newsize)); /* for making it bigger: clear out all the unused entries */ for(i=(*f)->nr_glyph;iglyph[i]=NULL; } (*f)->nr_glyph=newsize; } /* sets an entry in a font to a particular glyph */ static void set_font_glyph(struct bdf_font **f, unsigned id, struct bdf_glyph *g) { if(id>=(*f)->nr_glyph) { grow_font(f,(id | 255) + 1); /* round it up to the next page */ } if((*f)->glyph[id]) { unset_font_glyph(f, id); } (*f)->glyph[id]=g; } /* frees a font and derefences every glyph in the font */ static void free_font(struct bdf_font **f) { unsigned i; for(i=0;i<(*f)->nr_glyph;i++) { unset_font_glyph(f,i); } free(*f); *f=NULL; } /************************************************************************** * BDF parse functions **************************************************************************/ static void stripnl(char *line) { int tmp; tmp=strlen(line); if(tmp && (line[tmp-1]=='\n' || line[tmp-1]=='\r')) line[--tmp]=0; if(tmp && (line[tmp-1]=='\n' || line[tmp-1]=='\r')) line[--tmp]=0; } static int bdf_readline(FILE *f, char *line, int line_len, char *key, int key_len, char **param) { memset(line,'X',line_len); if(fgets(line,line_len,f)) { stripnl(line); line_len=strlen(line); while(key_len && line_len && !isspace(*line)) { *key=*line; key_len--; key++; line_len--; line++; } *key=0; while(line_len && isspace(*line)) { line_len--; line++; } *param=line; return 1; } return 0; } static unsigned hex(char ch) { const char tab[] = { ['0']=0, ['1']=1, ['2']=2, ['3']=3, ['4']=4, ['5']=5, ['6']=6, ['7']=7, ['8']=8, ['9']=9, ['a']=10, ['b']=11, ['c']=12, ['d']=13, ['e']=14, ['f']=15, ['A']=10, ['B']=11, ['C']=12, ['D']=13, ['E']=14, ['F']=15, }; unsigned ret; ret= ((unsigned char)chundefined=strtol(param, 0, 0); } else if(strcasecmp(key,"FONT_ASCENT")==0) { (*fnt)->descent=strtol(param, 0, 0); } else if(strcasecmp(key,"FONT_DESCENT")==0) { (*fnt)->ascent=strtol(param, 0, 0); } else { DEBUG_INFO("ignoring unknown property: '%s'\n", key); return 0; } } return 1; } static int bdf_readbitmap(FILE *f, struct bdf_font **fnt, struct bdf_glyph *glyph, unsigned id) { char buf[1024]; unsigned x, y; unsigned char *bl; #ifdef BDF_INVERT bl=glyph->bitmap+glyph->pitch*(glyph->height-1); #else bl=glyph->bitmap; #endif for(y=0;yheight;y++) { if(!fgets(buf,sizeof buf,f)) return 0; for(x=0;x<((glyph->width+3)/4);x++) { /* 4-bits per hex digit */ unsigned v; if(!isxdigit(buf[x])) { DEBUG_WARN("Invalid BITMAP section for glyph 0x%04x\n", id); break; /* ignore the weird data */ } v=hex(buf[x]); if(x%2==0) v<<=4; bl[x/2]|=v; } #ifdef BDF_INVERT bl-=glyph->pitch; #else bl+=glyph->pitch; #endif } set_font_glyph(fnt, id, glyph); return 1; } /************************************************************************** * External Functions **************************************************************************/ void freeFont(struct bdf_font **f) { free_font(f); } struct bdf_font *loadFont(const char *filename) { FILE *f; struct bdf_font *ret; char line[1024]; char key[64]; char *param; int line_count; int bdf_state; int nr_properties; /* number of property entries */ int nr_chars; /* numner in CHARS section */ int current_glyph_id=-1; /* current glyph we are working on */ struct bdf_glyph *current_glyph=0; DEBUG_INFO("loading font '%s'\n", filename); f=fopen(filename,"r"); if(!f) { char cwd[PATH_MAX]; perror(filename); getcwd(cwd, sizeof cwd); fprintf(stderr, "current directory: \"%s\"\n", cwd); return NULL; } ret=make_font(256); bdf_state=0; line_count=0; nr_chars=0; while(bdf_readline(f,line,sizeof line,key,sizeof key,¶m)) { line_count++; switch(bdf_state) { case 0: /* looking for startfont */ if(strcasecmp(key,"STARTFONT")==0 && strcmp(param,"2.1")==0) { bdf_state=1; } else { fprintf(stderr,"Not a BDF font file!\n"); goto fail_out; /* failure */ } break; case 1: /* STARTFONT: general/global settings. ignore unknown */ if(strcasecmp(key,"STARTPROPERTIES")==0) { bdf_state=2; /* process the properties */ nr_properties=strtol(param,NULL,10); bdf_readproperties(f, &ret, nr_properties); } else if(strcasecmp(key,"CHARS")==0) { bdf_state=3; /* process the chars */ nr_chars=strtol(param,NULL,10); } else if(strcasecmp(key,"ENDFONT")==0) { DEBUG_INFO("loaded font '%s' successfully.\n", filename); return ret; /* success */ } break; case 2: /* STARTPROPERTIES */ if(strcasecmp(key,"ENDPROPERTIES")==0) { bdf_state=1; /* back to main part */ } break; case 3: /* CHARS */ if(nr_chars-->0) { if(strcasecmp(key,"STARTCHAR")==0) { bdf_state=4; /* process a character */ } } else { bdf_state=1; /* back to the main part */ } break; case 4: /* STARTCHAR */ if(strcasecmp(key,"ENDCHAR")==0) { current_glyph=0; current_glyph_id=-1; if(nr_chars>0) { bdf_state=3; /* back to char section */ } else { bdf_state=1; /* back to main section */ } } else if(strcasecmp(key,"ENCODING")==0) { int tmp; char *endptr; tmp=strtol(param, &endptr, 10); if(!endptr || *endptr) { DEBUG_ERROR("%s:could not parse 'ENCODING' tag\n", filename); goto fail_out; } current_glyph_id=tmp; } else if(strcasecmp(key,"BBX")==0) { int width, height; int xofs, yofs; if(sscanf(param, "%d %d %d %d", &width, &height, &xofs, &yofs)!=4) { DEBUG_ERROR("%s:could not parse 'BBX' tag\n", filename); goto fail_out; } current_glyph=make_glyph(width, height); current_glyph->xofs=xofs; current_glyph->yofs=yofs; } else if(strcasecmp(key,"BITMAP")==0) { if(!current_glyph || current_glyph_id==-1) { DEBUG_ERROR("%s: 'ENCODING' and 'BBX' must proceed 'BITMAP' tag\n", filename); goto fail_out; } /* bitmap data reader */ bdf_readbitmap(f, &ret, current_glyph, current_glyph_id); } else if(strcasecmp(key,"SWIDTH")==0) { /* Ignored */ } else if(strcasecmp(key,"DWIDTH")==0) { /* Ignored */ } else if(strcasecmp(key,"FONTNAME_REGISTRY")==0) { /* Ignored */ } #ifdef BDF_WARN_UNKNOWN_TAGS else { DEBUG_WARN("%s: Unknown tag '%s' for glyph %d\n", filename, key, current_glyph_id); } #endif break; default: abort(); } } fail_out: fprintf(stderr, "%s: could not load font.\n", filename); free_font(&ret); return NULL; } /************************************************************************** * example code - outputs C or ASCII dump of a font **************************************************************************/ #ifdef STAND_ALONE #include #include #include /* turns a string into a C identifier */ static void make_c_name(char *buf, size_t len, const char *name) { assert(buf!=NULL); assert(len!=0); assert(name!=NULL); while(isdigit(*name)) { const char *digitname[] = { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine" }; int res; res=snprintf(buf, len, "%s_", digitname[*name-'0']); if((unsigned)res>len) res=len; name++; buf+=res; len-=res; } while(*name && len>2) { if(isalnum(*name)) *buf++=*name; else *buf++='_'; name++; len--; } *buf=0; } /* output will be inverted if BDF_INVERT was define */ static void dumpFont_c(struct bdf_font *f, const char *name, int include_comments) { char varname[32]; unsigned char *b; unsigned i, x, y; /* TODO: give g->xofs and g->yofs */ make_c_name(varname, sizeof varname, name); fprintf(stdout, "unsigned char %s_data[] = {\n", varname); for(i=BDF_DUMP_START;i<=BDF_DUMP_END && inr_glyph;i++) { struct bdf_glyph *g=f->glyph[i]; if(!g) g=f->glyph[' ']; /* fallback to blank */ if(!g) abort(); b=g->bitmap; fprintf(stdout, "/* '%c' - %ux%u - xofs:%d yofs:%d */\n", i, g->width, g->height, g->xofs, g->yofs); for(y=0;yheight;y++,b+=g->pitch) { fprintf(stdout, "\t"); for(x=0;xpitch;x++) { fprintf(stdout, "%3hhu,", b[x]); } if(include_comments) { fprintf(stdout, "\t/* "); for(x=0;xwidth;x++) { unsigned char ch; ch=b[x/CHAR_BIT]&(1<<((~x)%CHAR_BIT)) ? '#' : '.'; fputc(ch, stdout); } fprintf(stdout, " */"); } fprintf(stdout, "\n"); } } } /* takes BDF_INVERT into account so output is always correctly oriented */ static void dumpFont_ascii(struct bdf_font *f) { unsigned i, x, y; char ch; for(i=BDF_DUMP_START;i<=BDF_DUMP_END && inr_glyph;i++) { struct bdf_glyph *g=f->glyph[i]; printf("'%c' %ux%u%+d%+d bpl:%u\n", i, g->width, g->height, g->xofs, g->yofs, g->pitch); #ifdef BDF_INVERT for(y=g->height;y-->0;) { #else for(y=0;yheight;y++) { #endif for(x=0;xwidth;x++) { ch=g->bitmap[x/CHAR_BIT + y*g->pitch]&(1<<((~x)%CHAR_BIT)) ? '#' : '.'; printf("%c", ch); } printf("\n"); } } } /* -c : dump in c mode * -a : dump in ascii mode */ int main(int argc, char **argv) { struct bdf_font *fnt; int i; int dump_c_fl=0; for(i=1;i