shithub: fnt

ref: acc2272a84dfe957c57c26768144b7a9ddd5da27
dir: /otfpriv.h/

View raw version
struct Range {
	int start;
	int len;
	int prevoff;
	Range *par;
};

#define rchr(s) (be ? ((s)[0]<<8 | (s)[1]) : ((s)[1]<<8 | (s)[0]))

static u8int mark[] = {0x00, 0x00, 0xc0, 0xe0, 0xf0};

static int
utf16to8(u8int *o, int osz, u8int *s, int sz)
{
	u32int c, c2;
	int i, wr, j, be;

	i = 0;
	be = 1;
	if(s[0] == 0xfe && s[1] == 0xff)
		i += 2;
	else if(s[0] == 0xff && s[1] == 0xfe){
		be = 0;
		i += 2;
	}

	for(; i < sz-1 && osz > 1;){
		c = rchr(&s[i]);
		i += 2;
		if(c >= 0xd800 && c <= 0xdbff && i < sz-1){
			c2 = rchr(&s[i]);
			if(c2 >= 0xdc00 && c2 <= 0xdfff){
				c = 0x10000 | (c - 0xd800)<<10 | (c2 - 0xdc00);
				i += 2;
			}else
				return -1;
		}else if(c >= 0xdc00 && c <= 0xdfff)
			return -1;

		if(c < 0x80)
			wr = 1;
		else if(c < 0x800)
			wr = 2;
		else if(c < 0x10000)
			wr = 3;
		else
			wr = 4;

		osz -= wr;
		if(osz < 1)
			break;

		o += wr;
		for(j = wr; j > 1; j--){
			*(--o) = (c & 0xbf) | 0x80;
			c >>= 6;
		}
		*(--o) = (u8int)c | mark[wr];
		o += wr;
	}

	*o = 0;
	return i;
}

static char *
strtoutf8(Otf *o, u8int *in, int sz)
{
	char *s;

	// FIXME this is not sufficient, obviously - need more platform/encoding handling
	if(o->platformID == 0 || (o->platformID == 3 && o->encodingID == 1)){
		s = malloc(sz/2+1);
		utf16to8((u8int*)s, sz/2+1, in, sz);
	}else{
		s = malloc(sz+1);
		memcpy(s, in, sz);
		s[sz] = 0;
	}
	return s;
}

Otf *
otfopen(char *path)
{
	Otf *o;
	Biobuf *f;

	if((f = Bopen(path, OREAD)) == nil)
		return nil;
	if((o = calloc(1, sizeof(*o))) == nil){
		werrstr("no memory");
		Bterm(f);
	}else{
		o->f = f;
	}
	return o;
}

void
otfclose(Otf *o)
{
	if(o == nil)
		return;
	// FIXME traverse and free everything
	free(o);
}

static int
otfpushrange(Otf *o, int off, int len)
{
	Range *r;
	int x;

	r = nil;
	if(o->r != nil){
		if(len < 0)
			len = o->r->len - off;
		if(len < 0 || off+len > o->r->len){
			werrstr("range overflow (len %d) by %d bytes", len, off+len - o->r->len);
			goto err;
		}
		off += o->r->start;
	}else if(len < 0){
		len = 0x7fffffff;
	}
	if((r = malloc(sizeof(*r))) == nil){
		werrstr("no memory");
		goto err;
	}
	r->par = o->r;
	r->start = off;
	r->len = len;
	r->prevoff = o->off;
	if((x = Bseek(o->f, off, 0)) != off){
		werrstr("seek offset: need %d, got %d", off, x);
		goto err;
	}
	o->off = off;
	o->r = r;
	return 0;
err:
	free(r);
	return -1;
}

static int
otfpoprange(Otf *o)
{
	Range *r;
	int x;

	r = o->r;
	if(r == nil){
		werrstr("pop without push");
		goto err;
	}
	if((x = Bseek(o->f, r->prevoff, 0)) != r->prevoff){
		werrstr("seek offset: need %d, got %d", r->prevoff, x);
		goto err;
	}
	o->off = r->prevoff;
	o->r = r->par;
	free(r);
	return 0;
err:
	return -1;
}

static u8int *
otfreadn(Otf *o, int n)
{
	Range *r;
	u8int *b;
	int x;

	r = o->r;
	if(r != nil && o->off+n > r->start+r->len){
		werrstr("need %d at %d, have %d at %d", n, o->off, r->len, r->start);
		goto err;
	}
	if(n > o->bufsz){
		if((b = realloc(o->buf, n)) == nil){
			werrstr("no memory");
			goto err;
		}
		o->buf = b;
		o->bufsz = n;
	}
	if((x = Bread(o->f, o->buf, n)) != n){
		werrstr("need %d, got %d; off %d", n, x, o->off);
		goto err;
	}
	o->off += n;

	return o->buf;
err:
	return nil;
}

static int
otfarray(Otf *o, void **arr_, void *fun_, int elsz, int num)
{
	int i;
	int (*fun)(Otf*, void*);
	u8int *arr;

	if((arr = calloc(num, elsz)) == nil){
		werrstr("no memory");
		goto err;
	}
	fun = fun_;
	for(i = 0; i < num; i++){
		if(fun(o, arr + i*elsz) < 0)
			goto err;
	}
	*arr_ = arr;
	return 0;
err:
	free(arr);
	return -1;
}

int indentΔ = 2;

static int
Tfmt(Fmt *f)
{
	Tm t;
	s64int v = va_arg(f->args, s64int);
	return fmtprint(f, "%τ", tmfmt(tmtime(&t, v, nil), nil));
}

static int
Vfmt(Fmt *f)
{
	u32int v = va_arg(f->args, u32int);
	return fmtprint(f, "%d.%d", v>>16, v&0xffff);
}

static int
tfmt(Fmt *f)
{
	u32int v = va_arg(f->args, u32int);
	return fmtprint(f, "%c%c%c%c", v>>24, v>>16, v>>8, v>>0);
}

void
otfinit(void)
{
	tmfmtinstall();
	fmtinstall('V', Vfmt);
	fmtinstall('T', Tfmt);
	fmtinstall('t', tfmt);
}