/* bdf.c : BDF font loader */ /* Copyright 2003 Jon Mayo * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: The above copyright * notice and this permission notice shall be included in all copies or * substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ /* $Id: bdf.c 3 2003-06-01 08:56:22Z orange $ */ /*:._.:*~*:._.:*~*:._.:*~*:._.:*~*:._.:*~*:._.:*~*:._.:*~*:._.:*~*:._.:*/ #include #include #include #include #include #include #include "debug.h" #include "bdf.h" #define GLYPH_ALIGN 4 /* create an empty glyph */ static struct glyph *make_glyph(int width, int height) { struct glyph *ret; unsigned pitch; /* round up to 32-bit boundries */ pitch=((width+(GLYPH_ALIGN*8-1))/(GLYPH_ALIGN*8))*32/8; /* append the data onto the end of the struct and malloc the whole thing. */ ret=calloc(1,sizeof(struct 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 */ inline void free_glyph(struct glyph *g) { if(!g->rc--) { /* when the RC would drop below 0 free it */ free(g); } } /* reference an existing glyph */ inline struct glyph *take_glyph(struct glyph *g) { g->rc++; return g; } /* determines the number to pass to a malloc/realloc for a font structure with * a particular count */ inline int font_alloc_size(int count) { return sizeof(struct font)+count*sizeof(struct glyph*); } /* creates a new font structure. */ static struct font *make_font(int initial_size) { struct 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 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 font **f, int newsize) { int 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 font **f, unsigned id, struct 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 font **f) { int 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)ch>i)&1)+'0' , stdout); } } static int bdf_readproperties(FILE *f, struct font **fnt, unsigned nr_properties) { int i; char line[1024]; char key[64]; char *param; for(i=0;iundefined=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'", line); return 0; } } return 1; } static int bdf_readbitmap(FILE *f, struct font **fnt, struct glyph *glyph, unsigned id) { char buf[1024]; int x, y; uint8_t *bl; bl=glyph->bitmap+glyph->pitch*(glyph->height-1); for(y=0;yheight;y++) { if(!fgets(buf,sizeof buf,f)) return 0; for(x=0;x<(glyph->width/4);x++) { unsigned v; if(!isxdigit(buf[x])) { DEBUG_WARN("Invalid BITMAP section for glyph 0x%04x\n", id); break; } v=hex(buf[x]); if(x%2==0) v<<=4; bl[x/2]|=v; } bl-=glyph->pitch; } set_font_glyph(fnt, id, glyph); return 1; } /*:._.:*~*:._.:*~*:._.:*~*:._.:*~*:._.:*~*:._.:*~*:._.:*~*:._.:*~*:._.:*/ /* External Functions */ void freeFont(struct font **f) { free_font(f); } struct font *loadFont(const char *filename) { FILE *f; struct 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 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); } 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 { DEBUG_WARN("%s: Unknown tag '%s' for glyph %d\n", filename, key, current_glyph_id); } */ break; default: abort(); } } fail_out: fprintf(stderr, "%s: could not load font.\n", filename); free_font(&ret); return NULL; }