#include #include #include /* entry has 1 byte in widtab + 8 bytes per tile */ #define ufxlen(n) ((n)*(n)*8) #define max(a, b) ((a) > (b) ? (a) : (b)) #define min(a, b) ((a) < (b) ? (a) : (b)) #define warn(fmt, ...) fprint(2, "%s: " fmt "\n", argv0, __VA_ARGS__) enum{ MR_BLANK, MR_SINGLE, MR_RANGE }; struct MapRule{ struct MapRule *next; uchar ty; uint start, end; }; struct Mapping{ struct Mapping *next; uint linum; Rune start; union {Rune end; uint idx;}; struct MapRule *rules; }; struct MapFile{ struct MapFile *next; char *file; struct Mapping *mappings; }; struct GlyphInfo{ uchar top, bottom, left, width; }; struct RawImage{ char chan[12], minx[12], miny[12], maxx[12], maxy[12]; uchar data[]; }; uint fwidth, nglyphs, glyphlen, glyphedge, baseline; uchar setconv, donaive, doholes; struct MapFile *mapfiles, *lastmf; uchar wasmferr, wasreaderr; struct GlyphInfo *glyphs; struct RawImage *glyphbuf; void bioerr(char *s) { wasreaderr = 1; wasmferr = 1; fprint(2, "%s: %s\n", argv0, s); } char * readtoken(Biobuf *b, char **line, uint *linum) { char *ret, *p; while(*line != nil){ p = *line; while(p[0] != '\0' && strchr("\t\r ", p[0]) != nil) p++; if(strchr("#;\n", p[0]) != nil){ *line = Brdline(b, '\n'); (*linum)++; continue; } ret = p; while(strchr("\t\r\n ", p[0]) == nil) p++; *line = p; if(p[0] != '\n') (*line)++; p[0] = '\0'; return ret; } return nil; } #pragma varargck argpos mferr 3 void mferr(char *file, uint linum, char *fmt, ...) { char buf[1024]; va_list va; va_start(va, fmt); vsnprint(buf, sizeof(buf), fmt, va); va_end(va); if(linum != 0) fprint(2, "%s: %s:%ud: %s\n", argv0, file, linum, buf); else fprint(2, "%s: %s: %s\n", argv0, file, buf); wasmferr = 1; } void _parsemapfile(Biobuf *b, struct MapFile *mf) { char *line, *tok, *p; uint linum = 1; struct Mapping *mlast = nil; struct MapRule *rlast; wasreaderr = 0; line = Brdline(b, '\n'); if(line == nil){ if(!wasreaderr) warn("warn: %s: empty file\n", mf->file); return; } tok = readtoken(b, &line, &linum); while(tok != nil){ if(strcmp(tok, "mapping") == 0){ if(mlast == nil) mf->mappings = mlast = malloc(sizeof(struct Mapping)); else mlast = mlast->next = malloc(sizeof(struct Mapping)); mlast->linum = linum; mlast->rules = nil; mlast->next = nil; tok = readtoken(b, &line, &linum); if(tok == nil){ mferr(mf->file, linum, "unexpected eof"); return; } mlast->start = strtoul(tok, &p, 0); if(tok == p){ mferr(mf->file, linum, "empty lower rune bound"); return; } tok = p; if(tok[0] == '\0'){ /* 1:1 mapping */ tok = readtoken(b, &line, &linum); if(tok == nil){ mferr(mf->file, linum, "unexpected eof"); return; } mlast->idx = strtoul(tok, &p, 0); if(tok == p || p[0] != '\0'){ mferr(mf->file, linum, "bad glyph index"); return; } tok = readtoken(b, &line, &linum); continue; }else if(tok[0] != '-'){ mferr(mf->file, linum, "bad rune range"); return; } tok++; /* many:many mapping */ mlast->end = strtoul(tok, &p, 0); if(tok == p){ mferr(mf->file, linum, "empty upper rune bound"); return; }else if(p[0] != '\0'){ mferr(mf->file, linum, "bad upper rune bound"); return; } rlast = nil; while((tok = readtoken(b, &line, &linum)) != nil){ if(rlast == nil) mlast->rules = rlast = malloc(sizeof(struct MapRule)); else rlast = rlast->next = malloc(sizeof(struct MapRule)); rlast->next = nil; if(strcmp(tok, "blank") == 0){ rlast->ty = MR_BLANK; continue; }else if(tok[0] == '-'){ /* e.g. -0x4f */ tok++; rlast->ty = MR_RANGE; rlast->start = 0; rlast->end = strtoul(tok, &p, 0); if(tok == p || p[0] != '\0'){ mferr(mf->file, linum, "bad upper glyph bound"); return; } continue; } rlast->start = strtoul(tok, &p, 0); if(tok == p) break; /* return to toplevel parser */ else if(p[0] == '\0'){ rlast->ty = MR_SINGLE; continue; }else if(p[0] != '-'){ mferr(mf->file, linum, "bad lower glyph bound"); return; } rlast->ty = MR_RANGE; tok = p+1; if(tok[0] == '\0'){ /* e.g. 0x80- */ rlast->end = nglyphs - 1; continue; } rlast->end = strtoul(tok, &p, 0); if(tok == p || p[0] != '\0'){ mferr(mf->file, linum, "bad upper glyph bound"); return; } } /* end while */ if(rlast == nil){ mferr(mf->file, linum, "unexpected eof"); return; } }else{ mferr(mf->file, linum, "unknown verb %s", tok); return; } } /* end while */ } void parsemapfile(struct MapFile *mf) { Biobuf *b; b = Bopen(mf->file, OREAD); if(b == nil){ fprint(2, "%s: %r\n", argv0); wasmferr = 1; return; } Blethal(b, bioerr); _parsemapfile(b, mf); Bterm(b); } void printmapfile(struct MapFile *mf) { struct Mapping *mp; struct MapRule *rp; print("mapfile: %s\n", mf->file); for(mp = mf->mappings; mp != nil; mp = mp->next){ if(mp->rules == nil) print("\t%ud: %ux => %ux\n", mp->linum, mp->start, mp->idx); else{ print("\t%ud: %ux - %ux =>\n", mp->linum, mp->start, mp->end); for(rp = mp->rules; rp != nil; rp = rp->next){ switch(rp->ty){ case MR_SINGLE: print("\t\t%ux\n", rp->start); break; case MR_RANGE: print("\t\t%ux - %ux\n", rp->start, rp->end); break; case MR_BLANK: print("\t\tblank\n"); break; } } } } } void addmapfile(char *file) { if(lastmf == nil) mapfiles = lastmf = malloc(sizeof(struct MapFile)); else lastmf = lastmf->next = malloc(sizeof(struct MapFile)); lastmf->file = file; lastmf->next = nil; lastmf->mappings = nil; parsemapfile(lastmf); } void checkmapfiles(void) { if(wasmferr) exits("mapfile"); } void guesswidth(char *file) { uint len; char c; len = strlen(file); c = file[len-1]; if('1' <= c && c <= '8' && strncmp(file+len-4, ".uf", 3) == 0){ fwidth = c - '0'; } } void guesslength(char *file, Dir *d) { if(nglyphs == 0){ if(d->length % (1+glyphlen) != 0) sysfatal("%s: can't determine file dimensions", file); nglyphs = d->length / (1+glyphlen); }else if(nglyphs * (1+glyphlen) > d->length) sysfatal("%s: file too short", file); } void guessdims(char *file) { Dir *d; if(fwidth < 1) guesswidth(file); if(fwidth < 1) sysfatal("%s: can't determine glyph width", file); glyphlen = ufxlen(fwidth); glyphedge = fwidth * 8; d = dirstat(file); if(d == nil) sysfatal("%r"); guesslength(file, d); free(d); } void loadwidths(Biobuf *b, char *file) { uint i; int n; for(i = 0; i < nglyphs; i++){ n = Bgetc(b); if(n < 0) sysfatal("%r"); if(n > glyphedge){ warn("%s: 0x%02x: bad width %d, truncating\n", file, i, n); n = glyphedge; } glyphs[i].width = n; } } void allocrawimage(void) { char buf[13]; uint imglen; /* cols * rows */ imglen = glyphedge * nglyphs*fwidth; glyphbuf = malloc(sizeof(struct RawImage) + imglen); memcpy(glyphbuf->chan, " k1 ", 12); memcpy(glyphbuf->minx, " 0 ", 12); memcpy(glyphbuf->miny, " 0 ", 12); snprint(buf, 12, "%11d ", nglyphs*fwidth*8); memcpy(glyphbuf->maxx, buf, 11); snprint(buf, 12, "%11d ", glyphedge); memcpy(glyphbuf->maxy, buf, 11); memset(glyphbuf->data, 0, imglen); } uchar testscanline(uchar *bitmap, int rowlen) { uchar mask, x; for(x = 0; x <= (rowlen-1)/8; x++){ /* ignore the right edge of the last byte */ mask = 0xff << (8-min(8, rowlen-x*8)); if((bitmap[x*glyphedge] & mask) != 0) return 1; } return 0; } void parseglyph(uchar *bitmap, struct GlyphInfo *ginfo, uint *botfreqs) { uint top, bot; int rowlen = ginfo->width; for(top = 0; top < glyphedge; top++) if(testscanline(bitmap+top, rowlen)) break; for(bot = glyphedge-1; bot >= top; bot--) if(testscanline(bitmap+bot, rowlen)) break; if(botfreqs != nil && top <= bot) /* don't count empty glyphs */ botfreqs[bot]++; ginfo->top = top; ginfo->bottom = bot; } void storeglyph(uint i, uchar *bitmap) { uint y, x; ulong dstidx, srcidx; for(x = 0; x < fwidth; x++){ for(y = 0; y < glyphedge; y++){ dstidx = (i*fwidth)+x+(y*fwidth*nglyphs); srcidx = y+(x*glyphedge); glyphbuf->data[dstidx] = bitmap[srcidx]; } } } void readglyphs(Biobuf *b, uint *botfreqs) { uint i; uchar *bitmap; long n; bitmap = malloc(glyphlen); for(i = 0; i < nglyphs; i++){ n = Bread(b, bitmap, glyphlen); if(n < 0) sysfatal("%r"); if(n != glyphlen) sysfatal("unexpected eof"); parseglyph(bitmap, glyphs+i, botfreqs); storeglyph(i, bitmap); } free(bitmap); } void loadglyphs(Biobuf *b) { uint i, *botfreqs, bestfreq; if(baseline < 1){ botfreqs = calloc(glyphedge, sizeof(uint)); readglyphs(b, botfreqs); bestfreq = 0; for(i = 0; i < glyphedge; i++){ if(botfreqs[i] > bestfreq){ baseline = i; bestfreq = botfreqs[i]; } } }else readglyphs(b, nil); } void loadufx(char *infile) { Biobuf *b; glyphs = malloc(nglyphs * sizeof(struct GlyphInfo)); b = Bopen(infile, OREAD); allocrawimage(); loadwidths(b, infile); loadglyphs(b); Bterm(b); } void usage(void) { fprint(2, "usage: %s [-nh] [-b baseline] [-m mapfile]" "[-l length] [-x width] in.ufx out/\n", argv0); exits("usage"); } void main(int argc, char **argv) { char *infile, *outdir; ARGBEGIN{ case 'l': nglyphs = atoi(EARGF(usage())); break; case 'x': fwidth = atoi(EARGF(usage())); if(1 > fwidth || fwidth > 8) sysfatal("-x: width must be in range 1-8"); break; case 'n': setconv = 1; donaive = 1; break; case 'h': setconv = 1; doholes = 1; break; case 'm': setconv = 1; addmapfile(EARGF(usage())); break; case 'b': baseline = atoi(EARGF(usage())); if(baseline < 1) sysfatal("-b: baseline must be in range 1-width*8"); break; default: usage(); }ARGEND; checkmapfiles(); if(argv0 == nil) argv0 = "ufx2font"; if(argc != 2) usage(); infile = argv[0]; outdir = argv[1]; guessdims(infile); if(baseline >= glyphedge) sysfatal("-b: baseline must be in range 1-width*8"); loadufx(infile); int fd = create(outdir, OWRITE, 0644); write(fd, glyphbuf, sizeof(struct RawImage) + (glyphedge*nglyphs*fwidth)); close(fd); }