344 lines · 7.1 KB · C
RawDownload
#include <u.h>#include <libc.h>#include <bio.h>#include <draw.h>#include <ctype.h>#include <ttf.h>#include "impl.h" voidttfunpack(TTFontU *f, char *p, ...){	va_list va;	int n;	uchar *p1;	u16int *p2;	u32int *p4;		va_start(va, p);	for(; *p != 0; p++)		switch(*p){		case 'b':			p1 = va_arg(va, u8int *);			*p1 = Bgetc(f->bin);			break;		case 'B':			p4 = va_arg(va, u32int *);			*p4 = Bgetc(f->bin);			break;		case 'w':			p2 = va_arg(va, u16int *);			*p2 = Bgetc(f->bin) << 8;			*p2 |= Bgetc(f->bin);			break;		case 'W':			p4 = va_arg(va, u32int *);			*p4 = Bgetc(f->bin) << 8;			*p4 |= Bgetc(f->bin);			break;		case 'S':			p4 = va_arg(va, u32int *);			*p4 = (char)Bgetc(f->bin) << 8;			*p4 |= Bgetc(f->bin);			break;		case 'l':			p4 = va_arg(va, u32int *);			*p4 = Bgetc(f->bin) << 24;			*p4 |= Bgetc(f->bin) << 16;			*p4 |= Bgetc(f->bin) << 8;			*p4 |= Bgetc(f->bin);			break;		case '.': Bgetc(f->bin); break;		case ' ': break;		default:			if(isdigit(*p)){				n = strtol(p, &p, 10);				p--;				Bseek(f->bin, n, 1);			}else abort();			break;		}} static intdirectory(TTFontU *f){	u32int scaler;	int i; 	ttfunpack(f, "lw .. .. ..", &scaler, &f->ntab);	if(scaler != 0x74727565 && scaler != 0x10000){		werrstr("unknown scaler type %#ux", scaler);		return -1;	}	f->tab = mallocz(sizeof(TTTable) * f->ntab, 1);	if(f->tab == nil) return -1;	for(i = 0; i < f->ntab; i++)		ttfunpack(f, "llll", &f->tab[i].tag, &f->tab[i].csum, &f->tab[i].offset, &f->tab[i].len);	return 0;} intttfgototable(TTFontU *f, char *str){	TTTable *t;	u32int tag;		tag = (u8int)str[0] << 24 | (u8int)str[1] << 16 | (u8int)str[2] << 8 | (u8int)str[3];	for(t = f->tab; t < f->tab + f->ntab; t++)		if(t->tag == tag){			Bseek(f->bin, t->offset, 0);			return t->len;		}	werrstr("no such table '%s'", str);	return -1;} static intttfparseloca(TTFontU *f){	int len, i;	u32int x;		len = ttfgototable(f, "loca");	if(len < 0) return -1;	x = 0;	if(f->longloca){		if(len > (f->numGlyphs + 1) * 4) len = (f->numGlyphs + 1) * 4;		for(i = 0; i < len/4; i++){			x = Bgetc(f->bin) << 24;			x |= Bgetc(f->bin) << 16;			x |= Bgetc(f->bin) << 8;			x |= Bgetc(f->bin);			f->ginfo[i].loca = x;		}	}else{		if(len > (f->numGlyphs + 1) * 2) len = (f->numGlyphs + 1) * 2;		for(i = 0; i < len/2; i++){			x = Bgetc(f->bin) << 8;			x |= Bgetc(f->bin);			f->ginfo[i].loca = x * 2;		}	}	for(; i < f->numGlyphs; i++)		f->ginfo[i].loca = x;	return 0;} static intttfparsehmtx(TTFontU *f){	int i;	u16int x, y;	int len;	int maxlsb;		len = ttfgototable(f, "hmtx");	if(len < 0)		return -1;	if(f->numOfLongHorMetrics > f->numGlyphs){		werrstr("nonsensical header: numOfLongHorMetrics > numGlyphs");		return -1;	}	for(i = 0; i < f->numOfLongHorMetrics; i++){		ttfunpack(f, "ww", &x, &y);		f->ginfo[i].advanceWidth = x;		f->ginfo[i].lsb = y;	}	maxlsb = (len - 2 * f->numOfLongHorMetrics) / 2;	if(maxlsb > f->numGlyphs){		werrstr("nonsensical header: maxlsb > f->numGlyphs");		return -1;	}	for(; i < maxlsb; i++){		ttfunpack(f, "w", &y);		f->ginfo[i].advanceWidth = x;		f->ginfo[i].lsb = y;	}	for(; i < f->numGlyphs; i++){		f->ginfo[i].advanceWidth = x;		f->ginfo[i].lsb = y;	}	return 0;} intttfparsecvt(TTFontU *f){	int len;	int i;	int x;	u8int *p;	short *w; 	len = ttfgototable(f, "cvt ");	if(len <= 0) return 0;	f->cvtu = mallocz(len, 1);	if(f->cvtu == 0) return -1;	Bread(f->bin, f->cvtu, len);	p = (u8int *) f->cvtu;	f->ncvtu = len / 2;	w = f->cvtu;	for(i = 0; i < f->ncvtu; i++){		x = (short)(p[0] << 8 | p[1]);		p += 2;		*w++ = x;	}	return 0;} static intttfparseos2(TTFontU *f){	int len;	u16int usWinAscent, usWinDescent;		len = ttfgototable(f, "OS/2 ");	if(len < 0)		return -1;	if(len < 78){		werrstr("OS/2 table too short");		return -1;	}	ttfunpack(f, "68 6 ww", &usWinAscent, &usWinDescent);	f->ascent = usWinAscent;	f->descent = usWinDescent;	return 0;} static voidttfcloseu(TTFontU *u){	int i; 	if(u == nil) return;	Bterm(u->bin);	for(i = 0; i < u->ncmap; i++)		free(u->cmap[i].tab);	free(u->cmap);	free(u->ginfo);	free(u->tab);	free(u->cvtu);	free(u);} voidttfclose(TTFont *f){	int i; 	if(f == nil) return;	if(--f->u->ref <= 0)		ttfcloseu(f->u);	for(i = 0; i < f->u->maxFunctionDefs; i++)		free(f->func[i].pgm);	free(f->hintstack);	free(f->func);	free(f->storage);	free(f->twilight);	free(f->twiorg);	free(f->cvt);	free(f);} static TTFont *ttfscaleu(TTFontU *u, int ppem){	TTFont *f;	int i;		f = mallocz(sizeof(TTFont), 1);	if(f == nil) return nil;	f->u = u;	u->ref++;	f->ppem = ppem;	f->ncvt = u->ncvtu;	f->cvt = malloc(sizeof(int) * u->ncvtu);	if(f->cvt == nil) goto error;	for(i = 0; i < u->ncvtu; i++)		f->cvt[i] = ttfrounddiv(u->cvtu[i] * ppem * 64, u->emsize);	f->hintstack = mallocz(sizeof(u32int) * u->maxStackElements, 1);	f->func = mallocz(sizeof(TTFunction) * u->maxFunctionDefs, 1);	f->storage = mallocz(sizeof(u32int) * u->maxStorage, 1);	f->twilight = mallocz(sizeof(TTPoint) * u->maxTwilightPoints, 1);	f->twiorg = mallocz(sizeof(TTPoint) * u->maxTwilightPoints, 1);	if(f->hintstack == nil || f->func == nil || f->storage == nil || f->twilight == nil || f->twiorg == nil) goto error;	f->ascentpx = (u->ascent * ppem + u->emsize - 1) / (u->emsize);	f->descentpx = (u->descent * ppem + u->emsize - 1) / (u->emsize);	if(ttfrunfpgm(f) < 0) goto error;	if(ttfruncvt(f) < 0) goto error;	return f; error:	ttfclose(f);	return nil;} TTFont *ttfopen(char *name, int ppem, int){	Biobuf *b;	TTFontU *u;		if(ppem < 0){		werrstr("invalid ppem argument");		return nil;	}	b = Bopen(name, OREAD);	if(b == nil)		return nil;	u = mallocz(sizeof(TTFontU), 1);	if(u == nil)		return nil;	u->bin = b;	u->nkern = -1;	if(directory(u) < 0) goto error;	if(ttfgototable(u, "head") < 0) goto error;	ttfunpack(u, "16 w W 16 wwww 6 w", &u->flags, &u->emsize, &u->xmin, &u->ymin, &u->xmax, &u->ymax, &u->longloca);	if(ttfgototable(u, "maxp") < 0) goto error;	ttfunpack(u, "4 wwwwwwwwwwwwww",		&u->numGlyphs, &u->maxPoints, &u->maxCountours, &u->maxComponentPoints, &u->maxComponentCountours,		&u->maxZones, &u->maxTwilightPoints, &u->maxStorage, &u->maxFunctionDefs, &u->maxInstructionDefs,		&u->maxStackElements, &u->maxSizeOfInstructions, &u->maxComponentElements, &u->maxComponentDepth);	u->ginfo = mallocz(sizeof(TTGlyphInfo) * (u->numGlyphs + 1), 1);	if(u->ginfo == nil)		goto error;	if(ttfgototable(u, "hhea") < 0) goto error;	ttfunpack(u, "10 wwww 16 w", &u->advanceWidthMax, &u->minLeftSideBearing, &u->minRightSideBearing, &u->xMaxExtent, &u->numOfLongHorMetrics);	if(ttfparseloca(u) < 0) goto error;	if(ttfparsehmtx(u) < 0) goto error;	if(ttfparsecvt(u) < 0) goto error;	if(ttfparsecmap(u) < 0) goto error;	if(ttfparseos2(u) < 0) goto error;	return ttfscaleu(u, ppem); error:	ttfcloseu(u);	return nil;} TTFont *ttfscale(TTFont *f, int ppem, int){	return ttfscaleu(f->u, ppem);} intttfrounddiv(int a, int b){	if(b < 0){ a = -a; b = -b; }	if(a > 0)		return (a + b/2) / b;	else		return (a - b/2) / b;} intttfvrounddiv(vlong a, int b){	if(b < 0){ a = -a; b = -b; }	if(a > 0)		return (a + b/2) / b;	else		return (a - b/2) / b;}