shithub: fnt

ref: f711b4426695620ce7646d3036f69275be73e59f
dir: /plan9/otf.c/

View raw version
#line 1 "otf.c.in"
/* this file is generated. do not modify. */
#include "otfsys.h"
#include "otf.h"
#include "otfpriv.h"

#define f2dot14(o) (f = b[2*o]<<8 | b[2*o+1], (f>>14)+(f&((1<<14)-1))/16384.0)

int indentΔ = 2;

#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(Otfile *f)
{
	Otf *o;

	if((o = calloc(1, sizeof(*o))) == nil){
		werrstr("no memory");
	}else{
		o->f = f;
		if(read_TableDirectory(o, &o->td) != 0){
			free(o);
			o = nil;
		}
	}
	return o;
}

void
otfprint(Otf *o, Otfile *out, int indent)
{
	print_TableDirectory(out, indent, o, &o->td);
}

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 = o->f->seek(o->f->aux, 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 = o->f->seek(o->f->aux, 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 void
otfpopranges(Otf *o)
{
	while(o->r != nil)
		otfpoprange(o);
}

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 = o->f->read(o->f->aux, 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;
}

void
print_BitmapGlyph(Otfile *f, int indent, Otf *o, BitmapGlyph *v)
{
	void *a = f->aux;
	int i;

	USED(o);
	if(v->bitDepth > 0)
		f->print(a, "%*s%s: %d\n", indent, "", "bitDepth", v->bitDepth);
	f->print(a, "%*s%s: %d\n", indent, "", "format", v->format);
	f->print(a, "%*s%s: %d\n", indent, "", "offset", v->offset);
	if(v->size > 0){
		f->print(a, "%*s%s: %d\n", indent, "", "pitchBits", v->pitchBits);
		f->print(a, "%*s%s: %d\n", indent, "", "size", v->size);
		f->print(a, "%*s%s: ", indent, "", "image");
		for(i = 0; i < v->size; i++)
			f->print(a, "%02"PRIx8, v->image[i]);
		f->print(a, "\n");
	}
	f->print(a, "%*s%s: %d\n", indent, "", "ppemX", v->ppemX);
	f->print(a, "%*s%s: %d\n", indent, "", "ppemY", v->ppemY);
	if(v->numComponents > 0){
		f->print(a, "%*s%s: %d\n", indent, "", "numComponents", v->numComponents);
		f->print(a, "%*s%s:\n", indent, "", "components");
		for(i = 0; i < v->numComponents; i++){
			f->print(a, "%*s%d:\n", indent+indentΔ, "", i);
			print_EbdtComponent(f, indent+indentΔ+indentΔ, o, v->components+i);
		}
	}
}

enum {
	CGLYPH_FL_WORD_ARGS = 1<<0,
	CGLYPH_FL_SCALE = 1<<3,
	CGLYPH_FL_OVERLAP_COMPOUND = 1<<10,
	CGLYPH_FL_UNSCALED_COMPONENT_OFFSET = 1<<12,
};

static int
read_ComponentGlyph(Otf *o, ComponentGlyph **out, int instr)
{
	ComponentGlyph *v;
	u8int *b;
	s16int f;

	if((v = calloc(1, sizeof(*v))) == nil){
nomem:
		werrstr("no memory");
		goto err;
	}

	if((b = otfreadn(o, 2*2)) == nil)
		goto err;
	v->flags = (b[0]<<8 | b[1]) & ~CGLYPH_FL_OVERLAP_COMPOUND;
	v->glyphIndex = b[2]<<8 | b[3];
	instr |= v->flags & CGLYPH_FL_INSTRUCTIONS;
	if(v->flags & CGLYPH_FL_UNSCALED_COMPONENT_OFFSET)
		v->flags &= ~(CGLYPH_FL_UNSCALED_COMPONENT_OFFSET | CGLYPH_FL_SCALED_COMPONENT_OFFSET);

	if((v->flags & CGLYPH_FL_WORD_ARGS) != 0 && (b = otfreadn(o, 2*2)) != nil){
		if(v->flags & CGLYPH_FL_SIGNED_XY){
			v->arg1 = (s16int)(b[0]<<8 | b[1]);
			v->arg2 = (s16int)(b[2]<<8 | b[3]);
		}else{
			v->arg1 = b[0]<<8 | b[1];
			v->arg1 = b[2]<<8 | b[3];
		}
	}else if((b = otfreadn(o, 2)) != nil){
		if(v->flags & CGLYPH_FL_SIGNED_XY){
			v->arg1 = (s8int)b[0];
			v->arg2 = (s8int)b[1];
		}else{
			v->arg1 = b[0];
			v->arg2 = b[1];
		}
	}
	if(b == nil)
		goto err;

	v->scaleX = 1.0;
	v->scaleY = 1.0;
	if((v->flags & CGLYPH_FL_SCALE) != 0 && (b = otfreadn(o, 2)) != nil){
		v->scaleX = f2dot14(0);
		v->scaleY = v->scaleX;
		v->flags &= ~CGLYPH_FL_SCALE;
		v->flags |= CGLYPH_FL_SCALE_XY;
	}else if((v->flags & CGLYPH_FL_SCALE_XY) != 0 && (b = otfreadn(o, 2*2)) != nil){
		v->scaleX = f2dot14(0);
		v->scaleY = f2dot14(1);
	}else if((v->flags & CGLYPH_FL_2X2_TRANSFORM) != 0 && (b = otfreadn(o, 2*2*2)) != nil){
		v->scaleX = f2dot14(0);
		v->scale01 = f2dot14(1);
		v->scale10 = f2dot14(2);
		v->scaleY = f2dot14(3);
	}
	if(b == nil)
		goto err;
	if((v->flags & CGLYPH_FL_MORE_COMPONENTS) != 0){
		if(read_ComponentGlyph(o, &v->next, instr) != 0){
			free(v);
			return -1;
		}
	}else if(instr != 0){
		if((b = otfreadn(o, 2)) == nil)
			goto err;
		v->numInstr = b[0]<<8 | b[1];
		if((v->instr = malloc(v->numInstr)) == nil)
			goto nomem;
		if(v->numInstr > 0 && (b = otfreadn(o, v->numInstr)) == nil)
			goto err;
		memcpy(v->instr, b, v->numInstr);
	}
	*out = v;
	return 0;
err:
	if(v != nil)
		free(v->instr);
	free(v);
	werrstr("ComponentGlyph: %r");
	return -1;
}

void
print_ComponentGlyph(Otfile *f, int indent, Otf *o, ComponentGlyph *v)
{
	void *a = f->aux;
	f->print(a, "%*s%s: %#"PRIx16"%s%s%s%s%s%s%s%s%s%s\n", indent, "", "flags", v->flags,
		(v->flags&CGLYPH_FL_SIGNED_XY)?" CGLYPH_FL_SIGNED_XY":"",
		(v->flags&CGLYPH_FL_ROUND_TO_GRID_XY)?" CGLYPH_FL_ROUND_TO_GRID_XY":"",
		(v->flags&CGLYPH_FL_SCALE)?" CGLYPH_FL_SCALE":"",
		(v->flags&CGLYPH_FL_MORE_COMPONENTS)?" CGLYPH_FL_MORE_COMPONENTS":"",
		(v->flags&CGLYPH_FL_SCALE_XY)?" CGLYPH_FL_SCALE_XY":"",
		(v->flags&CGLYPH_FL_2X2_TRANSFORM)?" CGLYPH_FL_2X2_TRANSFORM":"",
		(v->flags&CGLYPH_FL_INSTRUCTIONS)?" CGLYPH_FL_INSTRUCTIONS":"",
		(v->flags&CGLYPH_FL_METRICS)?" CGLYPH_FL_METRICS":"",
		(v->flags&CGLYPH_FL_OVERLAP_COMPOUND)?" CGLYPH_FL_OVERLAP_COMPOUND":"",
		(v->flags&CGLYPH_FL_SCALED_COMPONENT_OFFSET)?" CGLYPH_FL_SCALED_COMPONENT_OFFSET":""
	);
	f->print(a, "%*s%s: %"PRIu16"\n", indent, "", "glyphIndex", v->glyphIndex);
	if(v->arg1 != 0 || v->arg2 != 0){
		f->print(f->aux, "%*s%s: %d\n", indent, "", "arg1", v->arg1);
		f->print(f->aux, "%*s%s: %d\n", indent, "", "arg2", v->arg2);
	}
	if(v->flags & CGLYPH_FL_SCALE_XY){
		f->print(a, "%*s%s: %g\n", indent, "", "scaleX", v->scaleX);
		f->print(a, "%*s%s: %g\n", indent, "", "scaleY", v->scaleY);
	}else if(v->flags & CGLYPH_FL_2X2_TRANSFORM){
		f->print(a, "%*s%s: %g\n", indent, "", "scaleX", v->scaleX);
		f->print(a, "%*s%s: %g\n", indent, "", "scale01", v->scale01);
		f->print(a, "%*s%s: %g\n", indent, "", "scale10", v->scale10);
		f->print(a, "%*s%s: %g\n", indent, "", "scaleY", v->scaleY);
	}
	if(v->numInstr > 0 && v->instr != nil){
		f->print(a, "%*s%s: %"PRIu16"\n", indent, "", "numInstr", v->numInstr);
		f->print(a, "%*s%s:", indent, "", "instr");
		for(int i = 0; i < v->numInstr; i++)
			f->print(a, " %02"PRIx8, v->instr[i]);
		f->print(a, "\n");
	}
	if(v->next != nil){
		f->print(a, "%*s%s:\n", indent, "", "next");
		print_ComponentGlyph(f, indent+indentΔ, o, v->next);
	}
}

enum {
	GLYPH_FL_ON_CURVE_POINT = 1<<0,
	GLYPH_FL_X_SHORT_VECTOR = 1<<1,
	GLYPH_FL_Y_SHORT_VECTOR = 1<<2,
	GLYPH_FL_REPEAT = 1<<3,
	GLYPH_FL_X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR = 1<<4,
	GLYPH_FL_Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR = 1<<5,
	GLYPH_FL_OVERLAP_SIMPLE = 1<<6,
};

static int
read_SimpleGlyph(Otf *o, SimpleGlyph **out)
{
	SimpleGlyph *v;
	u8int *b, *flags;
	int i, n, nflags, c, nc;

	flags = nil;
	if((v = calloc(1, sizeof(*v))) == nil){
nomem:
		werrstr("no memory");
		goto err;
	}

	v->numEndPtsOfContours = o->numberOfContours;
	n = o->numberOfContours;
	if((b = otfreadn(o, 2*n+2)) == nil)
		goto err;
	if((v->endPtsOfContours = malloc(2*n)) == nil)
		goto nomem;
	for(int i = 0; i < n; i++)
		v->endPtsOfContours[i] = b[2*i+0]<<8 | b[2*i+1];
	v->numPoints = 1 + v->endPtsOfContours[o->numberOfContours-1];

	v->instructionLength = b[2*n+0]<<8 | b[2*n+1];
	n = v->instructionLength;
	if((b = otfreadn(o, n)) == nil)
		goto err;
	if((v->instructions = malloc(n)) == nil)
		goto nomem;
	memcpy(v->instructions, b, n);

	if((flags = malloc(v->numPoints)) == nil) /* not supposed to be more than that, 64Kb max */
		goto nomem;
	for(nflags = 0; nflags < v->numPoints;){
		if((b = otfreadn(o, 1)) == nil)
			goto err;
		if((flags[nflags++] = *b) & GLYPH_FL_REPEAT){
			flags[nflags-1] ^= GLYPH_FL_REPEAT;
			if((b = otfreadn(o, 1)) == nil)
				goto err;
			if(nflags + *b > v->numPoints){
				werrstr("repeat overflow (%d+%d > %d)", nflags, *b, v->numPoints);
				goto err;
			}
			memset(flags+nflags, flags[nflags-1], *b);
			nflags += *b;
		}
	}

	if((v->points = calloc(v->numPoints, sizeof(*v->points))) == nil)
		goto nomem;

	/* flags first */
	for(i = n = 0; i < nflags && n < v->numPoints; i++, n++)
		v->points[n].onCurve = (flags[i] & GLYPH_FL_ON_CURVE_POINT) != 0;

	/* read packed x coordinates */
	c = 0;
	for(i = n = 0; i < nflags && n < v->numPoints; i++){
		if((flags[i] & GLYPH_FL_X_SHORT_VECTOR) != 0){
			if((b = otfreadn(o, 1)) == nil)
				goto err;
			nc = *b;
			if((flags[i] & GLYPH_FL_X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR) == 0)
				nc = -nc;
			c = v->points[n++].x = c + nc;
		}else if((flags[i] & GLYPH_FL_X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR) != 0){
			v->points[n++].x = c;
		}else{
			if((b = otfreadn(o, 2)) == nil)
				goto err;
			c += (s16int)(b[0]<<8 | b[1]);
			v->points[n++].x = c;
		}
	}

	/* read packed y coordinates */
	c = 0;
	for(i = n = 0; i < nflags && n < v->numPoints; i++){
		if((flags[i] & GLYPH_FL_Y_SHORT_VECTOR) != 0){
			if((b = otfreadn(o, 1)) == nil)
				goto err;
			nc = *b;
			if((flags[i] & GLYPH_FL_Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR) == 0)
				nc = -nc;
			c = v->points[n++].y = c + nc;
		}else if((flags[i] & GLYPH_FL_Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR) != 0){
			v->points[n++].y = c;
		}else{
			if((b = otfreadn(o, 2)) == nil)
				goto err;
			c += (s16int)(b[0]<<8 | b[1]);
			v->points[n++].y = c;
		}
	}

	free(flags);
	*out = v;
	return 0;
err:
	free(flags);
	if(v != nil){
		free(v->endPtsOfContours);
		free(v->instructions);
		free(v->points);
	}
	free(v);
	werrstr("SimpleGlyph: %r");
	return -1;
}

void
print_SimpleGlyph(Otfile *f, int indent, Otf *o, SimpleGlyph *v)
{
	void *a = f->aux;
	USED(o);
	if(v->numEndPtsOfContours > 0){
		f->print(a, "%*s%s:", indent, "", "endPtsOfContours");
		for(int i = 0; i < v->numEndPtsOfContours; i++)
			f->print(a, " %"PRId16, v->endPtsOfContours[i]);
		f->print(a, "\n");
	}
	if(v->instructionLength > 0){
		f->print(a, "%*s%s: %"PRId16"\n", indent, "", "instructionLength", v->instructionLength);
		f->print(a, "%*s%s:", indent, "", "instructions");
		for(int i = 0; i < v->instructionLength; i++)
			f->print(a, " %02"PRIx8, v->instructions[i]);
		f->print(a, "\n");
	}
	if(v->numPoints > 0){
		f->print(a, "%*s%s: %d\n", indent, "", "numPoints", v->numPoints);
		f->print(a, "%*s%s:", indent, "", "points");
		for(int i = 0; i < v->numPoints; i++)
			f->print(a, " (%d,%d,%s)", v->points[i].x, v->points[i].y, v->points[i].onCurve?"on":"off");
		f->print(a, "\n");
	}
}

static Glyf *
bitebdt(Otf *o, Glyf *g, BitmapGlyph *bg)
{
	u8int *b;
	int i, n;

	if(o->ebdt == nil){
		for(i = 0; i < o->td.numTables; i++){
			TableRecord *rec = o->td.tableRecords+i;
			if(rec->tableTag == (u32int)('E'<<24|'B'<<16|'D'<<8|'T')){
				o->ebdt = rec;
				break;
			}
		}
		if(o->ebdt == nil){
			werrstr("no EBDT table");
			goto err;
		}
	}

	if(otfpushrange(o, o->ebdt->offset, o->ebdt->length) < 0)
		goto err;
	if(otfpushrange(o, bg->offset, bg->size) < 0)
		goto err;
	if(bg->format == 1 || bg->format == 2 || bg->format == 8){
		SmallGlyphMetrics sm;
		if(read_SmallGlyphMetrics(o, &sm) < 0)
			goto err;
		g->xMin = sm.bearingX;
		g->yMin = sm.bearingY - sm.height;
		g->xMax = sm.bearingX + sm.width;
		g->yMax = sm.bearingY;
	}else if(bg->format == 6 || bg->format == 7 || bg->format == 9){
		BigGlyphMetrics bm;
		if(read_BigGlyphMetrics(o, &bm) < 0)
			goto err;
		/* FIXME only horizontal metrics, also this is probably wrong */
		g->xMax = bm.width;
		g->yMax = bm.height;
		g->xMin = -(int)bm.horiBearingX;
		g->yMin = -(int)bm.horiBearingY;
		g->xMax += g->xMin;
		g->yMax += g->yMin;
	}

	bg->numComponents = 0;
	if(bg->format == 2 || bg->format == 5 || bg->format == 7){
		/* bit-aligned - last row is padded to a full byte */
		bg->pitchBits = g->xMax - g->xMin;
		bg->size = (bg->pitchBits*(g->yMax-g->yMin)*bg->bitDepth + 7)/8;
	}else{
		/* byte-aligned - every row is byte-aligned */
		n = ((g->xMax-g->xMin)*bg->bitDepth + 7)/8;
		bg->size = n * (g->yMax-g->yMin);
		bg->pitchBits = n * 8;
	}

	switch(bg->format){
	case 8:
		if(otfreadn(o, 1) == nil) /* skip uint8 pad */
			goto err;
		/* fallthrough */
	case 9:
		if((b = otfreadn(o, 2)) == nil)
			goto err;
		bg->numComponents = b[0]<<8 | b[1];
		if(otfarray(o, &bg->components, read_EbdtComponent, sizeof(EbdtComponent), bg->numComponents) < 0)
			goto err;
		/* no image, both 8 and 9 are component */
		bg->pitchBits = 0;
		bg->size = 0;
		/* fallthrough */
	case 1:
	case 2:
	case 5:
	case 6:
	case 7:
		g->bitmap = malloc(sizeof(*bg) + bg->size);
		memcpy(g->bitmap, bg, sizeof(*bg));
		if(bg->size > 0){
			if((b = otfreadn(o, bg->size)) == nil)
				goto err;
			memcpy(g->bitmap->image, b, bg->size);
		}
		break;
	}
	otfpoprange(o);
	otfpoprange(o);

	return g;
err:
	free(g->bitmap);
	free(g);
	otfpopranges(o);
	return nil;
}

static int
ppemcmp(BitmapGlyph *a, BitmapGlyph *b)
{
	if(a->ppemX != b->ppemX)
		return a->ppemX - b->ppemX;
	if(a->ppemY != b->ppemY)
		return a->ppemY - b->ppemY;
	/* FIXME - is there anything else to compare? bit depth? */
	return 0;
}

static Glyf *
bitglyf(Otf *o, Glyf *g, RasterOpts *opts)
{
	BitmapGlyph b, best, want;
	IndexSubtableRecord *isr;
	int i, j, k, n, r, found;
	IndexSubtable *is;
	BitmapSize *bs;

	want.ppemX = opts->ppemX;
	want.ppemY = opts->ppemY;
	found = 0;
	r = -1;

	g->type = GLYPH_BITMAP;
	bs = o->td.eblc->bitmapSizes;
	for(i = 0; i < (int)o->td.eblc->numSizes; i++, bs++){
		if(g->index < bs->startGlyphIndex)
			continue;
		if(g->index > bs->endGlyphIndex)
			break;

		b.bitDepth = bs->bitDepth;
		if(b.bitDepth != 1 && b.bitDepth != 8){
			werrstr("unsupported bit depth: %d", b.bitDepth);
			goto err;
		}
		b.ppemX = bs->ppemX;
		b.ppemY = bs->ppemY;
		isr = bs->indexSubtableList;
		for(j = 0; j < (int)bs->numberOfIndexSubtables; j++, isr++){
			if(g->index < isr->firstGlyphIndex)
				break;
			if(g->index > isr->lastGlyphIndex || (is = isr->indexSubtable) == nil)
				continue;

			b.format = is->imageFormat;
			b.offset = is->imageDataOffset;
			n = g->index - isr->firstGlyphIndex;
			switch(is->indexFormat){
			case 1:
				b.offset += is->sub1.sbitOffsets[n];
				b.size = is->sub1.sbitOffsets[n+1] - is->sub1.sbitOffsets[n];
				break;
			case 2:
				b.offset += n*is->sub2.imageSize;
				b.size = is->sub2.imageSize;
				/* FIXME - vertical metrics are not used here, also this is probably wrong */
				g->xMin = -is->sub2.bigMetrics.horiBearingX;
				g->yMin = is->sub2.bigMetrics.horiBearingY - is->sub2.bigMetrics.height;
				g->xMax = is->sub2.bigMetrics.width + g->xMin;
				g->yMax = is->sub2.bigMetrics.height + g->yMin;
				break;
			case 3:
				b.offset += is->sub3.sbitOffsets[n];
				b.size = is->sub3.sbitOffsets[n+1] - is->sub3.sbitOffsets[n];
				break;
			case 4:
				n = is->sub4.numGlyphs;
				for(k = 0; k < n; k++){
					if(is->sub4.glyphArray[k].glyphID == g->index)
						break;
				}
				if(k >= n)
					continue;
				b.offset += is->sub4.glyphArray[k].sbitOffset;
				b.size = is->sub4.glyphArray[k+1].sbitOffset - is->sub4.glyphArray[k].sbitOffset;
				break;
			case 5:
				n = is->sub5.numGlyphs;
				for(k = 0; k < n; k++){
					if(is->sub5.glyphIdArray[k] == g->index)
						break;
				}
				if(k >= n)
					continue;
				b.offset += k*is->sub5.imageSize;
				b.size = is->sub5.imageSize;
				/* FIXME - vertical metrics are not used here, also this is probably wrong */
				g->xMin = -is->sub5.bigMetrics.horiBearingX;
				g->yMin = is->sub5.bigMetrics.horiBearingY - is->sub5.bigMetrics.height;
				g->xMax = is->sub5.bigMetrics.width + g->xMin;
				g->yMax = is->sub5.bigMetrics.height + g->yMin;
				break;
			default:
				continue;
			}

			/* now try to choose the best match */
			if((n = ppemcmp(&b, &want)) == 0 || found == 0){
better:
				memcpy(&best, &b, sizeof(b));
				found = 1;
				if((r = n) == 0)
					goto done;
				continue;
			}
			/* both better, but the new is closer */
			if(n > 0 && (r > 0 && ppemcmp(&b, &best) < 0))
				goto better;
			/* both worse, but the new one is better so far */
			if(n < 0 && (r < 0 && ppemcmp(&b, &best) > 0))
				goto better;
		}
	}

done:
	if(found)
		return bitebdt(o, g, &best);

	werrstr("no bitmap");
err:
	free(g);
	return nil;
}

Glyf *
otfglyf(Otf *o, int index, RasterOpts *opts)
{
	static TableRecord noGlyf;
	int off, len, i;
	Glyf *g;

	if(index < 0 || index >= o->numGlyphs){
		werrstr("index out of range");
		return nil;
	}
	if(o->td.head == nil){
		werrstr("no head table");
		return nil;
	}
	if((g = calloc(1, sizeof(*g))) == nil){
		werrstr("no memory");
		return nil;
	}
	g->index = index;
	if(o->glyf == nil){
		for(i = 0; i < o->td.numTables; i++){
			TableRecord *rec = o->td.tableRecords+i;
			if(rec->tableTag == (u32int)('g'<<24|'l'<<16|'y'<<8|'f')){
				o->glyf = rec;
				break;
			}
		}
		if(o->glyf == nil)
			o->glyf = &noGlyf;
	}
	if(o->glyf != &noGlyf && o->td.loca != nil){
		if(o->indexToLocFormat == 0){
			off = (int)o->td.loca->shortOffsets[index]*2;
			len = (int)o->td.loca->shortOffsets[index+1]*2 - off;
		}else{
			off = o->td.loca->longOffsets[index];
			len = o->td.loca->longOffsets[index+1] - off;
		}

		if(len > 0){
			if(otfpushrange(o, o->glyf->offset, o->glyf->length) < 0){
err:
				free(g);
				otfpopranges(o);
				return nil;
			}
			if(otfpushrange(o, off, len) < 0)
				goto err;
			if(read_Glyf(o, g) < 0)
				goto err;
			otfpoprange(o);
			otfpoprange(o);
			return g;
		}
		if(o->td.eblc == nil && o->td.ebdt == nil)
			return g;
	}
	if(o->td.eblc != nil && o->td.ebdt != nil)
		return bitglyf(o, g, opts);

	free(g);
	return nil;
}

int
otfglyfnum(Otf *o)
{
	return o->numGlyphs;
}

int
otfrune2glyph(Otf *o, Rune r)
{
	RuneMapper *m;
	int i, g;

	for(i = 0, m = o->td.cmap->mappers; i < o->td.cmap->numMappers; i++, m++){
		if((g = m->rune2glyph(m->aux, r)) >= 0)
			return g;
	}
	return -1;
}

Rune
otfglyph2rune(Otf *o, int g)
{
	RuneMapper *m;
	Rune r;
	int i;

	for(i = 0, m = o->td.cmap->mappers; i < o->td.cmap->numMappers; i++, m++){
		if((r = m->glyph2rune(m->aux, g)) != NoRune)
			return r;
	}
	return NoRune;
}

enum {
	PLAT_UNICODE,
	PLAT_MACINTOSH, /* "currently discouraged", unsupported */
	PLAT_ISO, /* deprecated, unsupported */
	PLAT_WINDOWS,
	PLAT_CUSTOM, /* deprecated, unsupported */
};

/* supported subtable formats: 4, 6, 10, 12, 13 */
/* FIXME: need to implement (higher to lower prio): 14 */
enum {
	ENC_UNICODE_1_0, /* deprecated, unsupported */
	ENC_UNICODE_1_1, /* deprecated, unsupported */
	ENC_UNICODE_ISO, /* deprecated, unsupported */
	ENC_UNICODE_2_0_BMP, /* subtable format 4, 6 */
	ENC_UNICODE_2_0_FULL, /* subtable format 10, 12 */
	ENC_UNICODE_VAR_SEQ, /* subtable format 14 */
	ENC_UNICODE_FULL, /* subtable format 13 (many-to-one) */

	ENC_WINDOWS_SYMBOL = 0, /* unsupported */
	ENC_WINDOWS_UNICODE_BMP, /* subtable format 4 */
	ENC_WINDOWS_SHIFTJIS, /* unsupported */
	ENC_WINDOWS_PRC, /* unsupported */
	ENC_WINDOWS_BIG5, /* unsupported */
	ENC_WINDOWS_WANSUNG, /* unsupported */
	ENC_WINDOWS_JOHAB, /* unsupported */
	ENC_WINDOWS_UNICODE_FULL = 10, /* subtable format 12 */
};

static int
cmap4rune2glyph(void *aux, Rune r)
{
	SubtableCmap4 *sc;
	int i, b, e, n, x, segC;

	if(r > 0xffff)
		return -1;
	sc = aux;
	segC = sc->segCountX2/2;
	for(b = 0, e = segC-1; b <= e; ){
		i = (b + e)/2;
		if(sc->endCode[i] < r)
			b = i + 1;
		else if(sc->startCode[i] > r)
			e = i - 1;
		else if(sc->idRangeOffset[i] == 0){
			x = r + sc->idDelta[i];
			if(x < 0)
				x += 65536;
			return x;
		}else{
			x = i + sc->idRangeOffset[i]/2 + (r - sc->startCode[i]) - segC;
			n = (sc->length-((8*2)+(sc->segCountX2*4)))/2;
			if(x < 0 || x >= n)
				break;
			return sc->glyphIdArray[x];
		}
	}
	return -1;
}

static Rune
cmap4glyph2rune(void *aux, int g)
{
	USED(aux); USED(g);
	/* FIXME - other mapper will hopefully pick up after */
	return 'x';
}

static int
cmap6rune2glyph(void *aux, Rune r)
{
	SubtableCmap6 *sc;

	sc = aux;
	if(r >= sc->firstCode){
		r -= sc->firstCode;
		if(r < sc->entryCount)
			return sc->glyphIdArray[r];
	}
	return -1;
}

static Rune
cmap6glyph2rune(void *aux, int g)
{
	SubtableCmap6 *sc;

	sc = aux;
	if(g >= 0 && g < sc->entryCount)
		return (Rune)sc->firstCode + g;
	return NoRune;
}

static int
cmap10rune2glyph(void *aux, Rune r)
{
	SubtableCmap10 *sc;

	sc = aux;
	if(r >= sc->startCharCode){
		r -= sc->startCharCode;
		if(r < sc->numChars)
			return sc->glyphIdArray[r];
	}
	return -1;
}

static Rune
cmap10glyph2rune(void *aux, int g)
{
	SubtableCmap10 *sc;

	sc = aux;
	if(g >= 0 && g < sc->numChars)
		return sc->startCharCode + g;
	return NoRune;
}

static int
cmap12rune2glyph(void *aux, Rune r)
{
	SubtableCmap12or13 *sc;
	MapGroup *m;
	int b, e, x;

	sc = aux;
	for(b = 0, e = sc->numGroups-1; b <= e; ){
		x = (b + e)/2;
		m = sc->groups + x;
		if(m->endCharCode < r)
			b = x + 1;
		else if(m->startCharCode > r)
			e = x - 1;
		else
			return m->startGlyphID + (r - m->startCharCode);
	}
	return -1;
}

static Rune
cmap12glyph2rune(void *aux, int g)
{
	SubtableCmap12or13 *sc;
	MapGroup *m;
	int i;

	sc = aux;
	for(i = 0, m = sc->groups; i < sc->numGroups; i++, m++){
		if(g >= m->startGlyphID && g <= m->startGlyphID+(m->endCharCode-m->startCharCode))
			return m->startCharCode + (g - m->startGlyphID);
	}
	return NoRune;
}

static int
cmap13rune2glyph(void *aux, Rune r)
{
	SubtableCmap12or13 *sc;
	MapGroup *m;
	int b, e, x;

	sc = aux;
	for(b = 0, e = sc->numGroups-1; b <= e; ){
		x = (b + e)/2;
		m = sc->groups + x;
		if(m->endCharCode < r)
			b = x + 1;
		else if(m->startCharCode > r)
			e = x - 1;
		else
			return m->startGlyphID;
	}
	return -1;
}

static Rune
cmap13glyph2rune(void *aux, int g)
{
	SubtableCmap12or13 *sc;
	MapGroup *m;
	int i;

	sc = aux;
	for(i = 0, m = sc->groups; i < sc->numGroups; i++, m++){
		if(g == m->startGlyphID)
			return m->startCharCode;/* this doesn't make sense for constant mapping */
	}
	return NoRune;
}

static int
otfcmapUnicode(TableCmap *c, EncodingRecord *er, int *parsed, int *unsupported)
{
	SubtableCmap *sc;

	sc = er->subtable;
	switch(er->encodingID){
	case ENC_UNICODE_2_0_BMP:
		if(sc->format == 4){
			c->mappers[c->numMappers].rune2glyph = cmap4rune2glyph;
			c->mappers[c->numMappers].glyph2rune = cmap4glyph2rune;
			c->mappers[c->numMappers++].aux = &sc->sub4;
			(*parsed)++;
			break;
		}
		if(sc->format == 6){
			c->mappers[c->numMappers].rune2glyph = cmap6rune2glyph;
			c->mappers[c->numMappers].glyph2rune = cmap6glyph2rune;
			c->mappers[c->numMappers++].aux = &sc->sub6;
			(*parsed)++;
			break;
		}
		(*unsupported)++;
		werrstr("2.0 bmp: fmt %d", sc->format);
		goto err;

	case ENC_UNICODE_2_0_FULL: /* this one is good */
		if(sc->format == 10){
			if(sc->sub10.numChars < 1){
				werrstr("2.0 full: no chars");
				goto err;
			}
			c->mappers[c->numMappers].rune2glyph = cmap10rune2glyph;
			c->mappers[c->numMappers].glyph2rune = cmap10glyph2rune;
			c->mappers[c->numMappers++].aux = &sc->sub10;
			(*parsed)++;
			break;
		}
		if(sc->format == 12){
			if(sc->sub12or13.numGroups < 1){
				werrstr("2.0 full: no groups");
				goto err;
			}
			c->mappers[c->numMappers].rune2glyph = cmap12rune2glyph;
			c->mappers[c->numMappers].glyph2rune = cmap12glyph2rune;
			c->mappers[c->numMappers++].aux = &sc->sub12or13;
			(*parsed)++;
			break;
		}
		(*unsupported)++;
		werrstr("2.0 full: fmt %d", sc->format);
		goto err;

	case ENC_UNICODE_VAR_SEQ:
		/* FIXME */
		break;

	case ENC_UNICODE_FULL:
		if(sc->format == 13){
			if(sc->sub12or13.numGroups < 1){
				werrstr("full: no groups");
				goto err;
			}
			c->mappers[c->numMappers].rune2glyph = cmap13rune2glyph;
			c->mappers[c->numMappers].glyph2rune = cmap13glyph2rune;
			c->mappers[c->numMappers++].aux = &sc->sub12or13;
			(*parsed)++;
			break;
		}
		(*unsupported)++;
		werrstr("full: fmt %d", sc->format);
		goto err;

	case ENC_UNICODE_1_0:
	case ENC_UNICODE_1_1:
	case ENC_UNICODE_ISO:
		(*unsupported)++;
		werrstr("deprecated encoding: %d", er->encodingID);
		goto err;
	default:
		(*unsupported)++;
		werrstr("unknown encoding: %d", er->encodingID);
		goto err;
	}

	return 0;
err:
	werrstr("unicode: %r");
	return -1;
}

static int
otfcmapWindows(TableCmap *c, EncodingRecord *er, int *parsed, int *unsupported)
{
	SubtableCmap *sc;

	sc = er->subtable;
	switch(er->encodingID){
	case ENC_WINDOWS_UNICODE_BMP:
		if(sc->format == 4){
			c->mappers[c->numMappers].rune2glyph = cmap4rune2glyph;
			c->mappers[c->numMappers].glyph2rune = cmap4glyph2rune;
			c->mappers[c->numMappers++].aux = &sc->sub4;
			(*parsed)++;
			break;
		}
		if(sc->format == 6){
			c->mappers[c->numMappers].rune2glyph = cmap6rune2glyph;
			c->mappers[c->numMappers].glyph2rune = cmap6glyph2rune;
			c->mappers[c->numMappers++].aux = &sc->sub6;
			(*parsed)++;
			break;
		}
		(*unsupported)++;
		werrstr("unicode bmp: fmt %d", sc->format);
		goto err;

	case ENC_WINDOWS_UNICODE_FULL:
		if(sc->format != 12){
			(*unsupported)++;
			werrstr("unicode full: fmt %d", sc->format);
			goto err;
		}
		if(sc->sub12or13.numGroups < 1){
			werrstr("unicode full: no groups");
			goto err;
		}
		c->mappers[c->numMappers].rune2glyph = cmap12rune2glyph;
		c->mappers[c->numMappers].glyph2rune = cmap12glyph2rune;
		c->mappers[c->numMappers++].aux = &sc->sub12or13;
		(*parsed)++;
		break;

	case ENC_WINDOWS_SYMBOL:
	case ENC_WINDOWS_SHIFTJIS:
	case ENC_WINDOWS_PRC:
	case ENC_WINDOWS_BIG5:
	case ENC_WINDOWS_WANSUNG:
	case ENC_WINDOWS_JOHAB:
		(*unsupported)++;
		werrstr("unsupported encoding: %d", er->encodingID);
		goto err;
	default:
		(*unsupported)++;
		werrstr("unknown encoding: %d", er->encodingID);
		goto err;
	}
	return 0;
err:
	werrstr("windows: %r");
	return -1;
}

static int
otfcmap(TableCmap *c)
{
	int i, parsed, unsupported;
	EncodingRecord *er;

	parsed = 0;
	unsupported = 0;
	for(i = 0, er = c->encodingRecords; i < c->numTables; i++, er++){
		if(c->numMappers >= nelem(c->mappers)) /* give up */
			break;

		switch(er->platformID){
		case PLAT_UNICODE:
			/* FIXME issue a warning if returned non-zero */
			if(otfcmapUnicode(c, er, &parsed, &unsupported) != 0)
				goto err;
			break;

		case PLAT_WINDOWS:
			/* FIXME issue a warning if returned non-zero */
			if(otfcmapWindows(c, er, &parsed, &unsupported) != 0)
				goto err;
			break;

		case PLAT_MACINTOSH:
		case PLAT_ISO:
		case PLAT_CUSTOM:
		default:
			unsupported++;
			break;
		}
	}
	if(parsed > 0)
		return 0;

	if(unsupported > 0)
		werrstr(" (%d unsupported)", unsupported);
	else
		werrstr("");
	werrstr("no usable records%r");
	/* FIXME - eventually this return should be removed */
	return 0;
err:
	werrstr("cmap: %r");
	return -1;
}


int
read_SubHeader(Otf *o, SubHeader *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 8)) == nil)
		goto err;
	v->firstCode = b[0]<<8 | b[1];
	v->entryCode = b[2]<<8 | b[3];
	v->idDelta = b[4]<<8 | b[5];
	v->idRangeOffset = b[6]<<8 | b[7];
	return 0;
err:
	werrstr("%s: %r", "SubHeader");
	return -1;
}

void
print_SubHeader(Otfile *f, int indent, Otf *o, SubHeader *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "firstCode", v->firstCode);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "entryCode", v->entryCode);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "idDelta", v->idDelta);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "idRangeOffset", v->idRangeOffset);
	USED(o);
}

int
read_MapGroup(Otf *o, MapGroup *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 12)) == nil)
		goto err;
	v->startCharCode = b[0]<<24 | b[1]<<16 | b[2]<<8 | b[3];
	v->endCharCode = b[4]<<24 | b[5]<<16 | b[6]<<8 | b[7];
	v->startGlyphID = b[8]<<24 | b[9]<<16 | b[10]<<8 | b[11];
	return 0;
err:
	werrstr("%s: %r", "MapGroup");
	return -1;
}

void
print_MapGroup(Otfile *f, int indent, Otf *o, MapGroup *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "startCharCode", v->startCharCode);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "endCharCode", v->endCharCode);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "startGlyphID", v->startGlyphID);
	USED(o);
}

int
read_SubtableCmap0(Otf *o, SubtableCmap0 *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 260)) == nil)
		goto err;
	v->length = b[0]<<8 | b[1];
	v->language = b[2]<<8 | b[3];
	for(int i = 0; i < 256; i++)
		v->glyphIdArray[i] = b[4+i*1];
	return 0;
err:
	werrstr("%s: %r", "SubtableCmap0");
	return -1;
}

void
print_SubtableCmap0(Otfile *f, int indent, Otf *o, SubtableCmap0 *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "length", v->length);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "language", v->language);
	for(int i = 0; i < 256; i++)
		f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "glyphIdArray", i, v->glyphIdArray[i]);
	USED(o);
}

int
read_SubtableCmap2(Otf *o, SubtableCmap2 *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 4)) == nil)
		goto err;
	v->length = b[0]<<8 | b[1];
	v->language = b[2]<<8 | b[3];
	return 0;
err:
	werrstr("%s: %r", "SubtableCmap2");
	return -1;
}

void
print_SubtableCmap2(Otfile *f, int indent, Otf *o, SubtableCmap2 *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "length", v->length);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "language", v->language);
	USED(o);
}

int
read_SubtableCmap4(Otf *o, SubtableCmap4 *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 12)) == nil)
		goto err;
	v->length = b[0]<<8 | b[1];
	v->language = b[2]<<8 | b[3];
	v->segCountX2 = b[4]<<8 | b[5];
	if(v->segCountX2 <= 0){
		werrstr("%s: invalid value: %d (%#ux)", "segCountX2", v->segCountX2, v->segCountX2);
		goto err;
	}
	v->searchRange = b[6]<<8 | b[7];
	v->entrySelector = b[8]<<8 | b[9];
	v->rangeShift = b[10]<<8 | b[11];
	if((b = otfreadn(o, (v->segCountX2/2)*2)) == nil)
		goto err;
	v->endCode = malloc((v->segCountX2/2)*sizeof(*v->endCode));
	for(int i = 0; i < (v->segCountX2/2); i++)
		v->endCode[i] = b[0+i*2]<<8 | b[1+i*2];
	if((b = otfreadn(o, 2)) == nil)
		goto err;
	USED(b);
	if((b = otfreadn(o, (v->segCountX2/2)*2)) == nil)
		goto err;
	v->startCode = malloc((v->segCountX2/2)*sizeof(*v->startCode));
	for(int i = 0; i < (v->segCountX2/2); i++)
		v->startCode[i] = b[0+i*2]<<8 | b[1+i*2];
	if((b = otfreadn(o, (v->segCountX2/2)*2)) == nil)
		goto err;
	v->idDelta = malloc((v->segCountX2/2)*sizeof(*v->idDelta));
	for(int i = 0; i < (v->segCountX2/2); i++)
		v->idDelta[i] = b[0+i*2]<<8 | b[1+i*2];
	if((b = otfreadn(o, (v->segCountX2/2)*2)) == nil)
		goto err;
	v->idRangeOffset = malloc((v->segCountX2/2)*sizeof(*v->idRangeOffset));
	for(int i = 0; i < (v->segCountX2/2); i++)
		v->idRangeOffset[i] = b[0+i*2]<<8 | b[1+i*2];
	if((b = otfreadn(o, ((v->length-((8*2)+(v->segCountX2*4)))/2)*2)) == nil)
		goto err;
	v->glyphIdArray = malloc(((v->length-((8*2)+(v->segCountX2*4)))/2)*sizeof(*v->glyphIdArray));
	for(int i = 0; i < ((v->length-((8*2)+(v->segCountX2*4)))/2); i++)
		v->glyphIdArray[i] = b[0+i*2]<<8 | b[1+i*2];
	return 0;
err:
	werrstr("%s: %r", "SubtableCmap4");
	return -1;
}

void
print_SubtableCmap4(Otfile *f, int indent, Otf *o, SubtableCmap4 *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "length", v->length);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "language", v->language);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "segCountX2", v->segCountX2);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "searchRange", v->searchRange);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "entrySelector", v->entrySelector);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "rangeShift", v->rangeShift);
	for(int i = 0; i < (v->segCountX2/2); i++)
		f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "endCode", i, v->endCode[i]);
	for(int i = 0; i < (v->segCountX2/2); i++)
		f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "startCode", i, v->startCode[i]);
	for(int i = 0; i < (v->segCountX2/2); i++)
		f->print(f->aux, "%*s%s[%d]: %d\n", indent, "", "idDelta", i, v->idDelta[i]);
	for(int i = 0; i < (v->segCountX2/2); i++)
		f->print(f->aux, "%*s%s[%d]: %d\n", indent, "", "idRangeOffset", i, v->idRangeOffset[i]);
	for(int i = 0; i < ((v->length-((8*2)+(v->segCountX2*4)))/2); i++)
		f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "glyphIdArray", i, v->glyphIdArray[i]);
	USED(o);
}

int
read_SubtableCmap6(Otf *o, SubtableCmap6 *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 8)) == nil)
		goto err;
	v->length = b[0]<<8 | b[1];
	v->language = b[2]<<8 | b[3];
	v->firstCode = b[4]<<8 | b[5];
	v->entryCount = b[6]<<8 | b[7];
	if((b = otfreadn(o, v->entryCount*2)) == nil)
		goto err;
	v->glyphIdArray = malloc(v->entryCount*sizeof(*v->glyphIdArray));
	for(int i = 0; i < v->entryCount; i++)
		v->glyphIdArray[i] = b[0+i*2]<<8 | b[1+i*2];
	return 0;
err:
	werrstr("%s: %r", "SubtableCmap6");
	return -1;
}

void
print_SubtableCmap6(Otfile *f, int indent, Otf *o, SubtableCmap6 *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "length", v->length);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "language", v->language);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "firstCode", v->firstCode);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "entryCount", v->entryCount);
	for(int i = 0; i < v->entryCount; i++)
		f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "glyphIdArray", i, v->glyphIdArray[i]);
	USED(o);
}

int
read_SubtableCmap8(Otf *o, SubtableCmap8 *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 8200)) == nil)
		goto err;
	v->length = b[0]<<8 | b[1];
	v->language = b[2]<<8 | b[3];
	for(int i = 0; i < 8192; i++)
		v->is32[i] = b[4+i*1];
	v->numGroups = b[8196]<<24 | b[8197]<<16 | b[8198]<<8 | b[8199];
	if(otfarray(o, &v->groups, read_MapGroup, sizeof(MapGroup), v->numGroups) < 0){
		werrstr("%s: %r", "groups");
		goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "SubtableCmap8");
	return -1;
}

void
print_SubtableCmap8(Otfile *f, int indent, Otf *o, SubtableCmap8 *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "length", v->length);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "language", v->language);
	for(int i = 0; i < 8192; i++)
		f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "is32", i, v->is32[i]);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "numGroups", v->numGroups);
	for(int i = 0; i < v->numGroups; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "groups", i);
		print_MapGroup(f, indent+indentΔ, o, &v->groups[i]);
	}
	USED(o);
}

int
read_SubtableCmap10(Otf *o, SubtableCmap10 *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 18)) == nil)
		goto err;
	v->reserved = b[0]<<8 | b[1];
	v->length = b[2]<<24 | b[3]<<16 | b[4]<<8 | b[5];
	v->language = b[6]<<24 | b[7]<<16 | b[8]<<8 | b[9];
	v->startCharCode = b[10]<<24 | b[11]<<16 | b[12]<<8 | b[13];
	v->numChars = b[14]<<24 | b[15]<<16 | b[16]<<8 | b[17];
	if((b = otfreadn(o, ((v->length-((2*2)+(4*4)))/2)*2)) == nil)
		goto err;
	v->glyphIdArray = malloc(((v->length-((2*2)+(4*4)))/2)*sizeof(*v->glyphIdArray));
	for(int i = 0; i < ((v->length-((2*2)+(4*4)))/2); i++)
		v->glyphIdArray[i] = b[0+i*2]<<8 | b[1+i*2];
	return 0;
err:
	werrstr("%s: %r", "SubtableCmap10");
	return -1;
}

void
print_SubtableCmap10(Otfile *f, int indent, Otf *o, SubtableCmap10 *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "reserved", v->reserved);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "length", v->length);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "language", v->language);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "startCharCode", v->startCharCode);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "numChars", v->numChars);
	for(int i = 0; i < ((v->length-((2*2)+(4*4)))/2); i++)
		f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "glyphIdArray", i, v->glyphIdArray[i]);
	USED(o);
}

int
read_SubtableCmap12or13(Otf *o, SubtableCmap12or13 *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 14)) == nil)
		goto err;
	v->reserved = b[0]<<8 | b[1];
	v->length = b[2]<<24 | b[3]<<16 | b[4]<<8 | b[5];
	v->language = b[6]<<24 | b[7]<<16 | b[8]<<8 | b[9];
	v->numGroups = b[10]<<24 | b[11]<<16 | b[12]<<8 | b[13];
	if(otfarray(o, &v->groups, read_MapGroup, sizeof(MapGroup), v->numGroups) < 0){
		werrstr("%s: %r", "groups");
		goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "SubtableCmap12or13");
	return -1;
}

void
print_SubtableCmap12or13(Otfile *f, int indent, Otf *o, SubtableCmap12or13 *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "reserved", v->reserved);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "length", v->length);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "language", v->language);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "numGroups", v->numGroups);
	for(int i = 0; i < v->numGroups; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "groups", i);
		print_MapGroup(f, indent+indentΔ, o, &v->groups[i]);
	}
	USED(o);
}

int
read_UnicodeRange(Otf *o, UnicodeRange *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 4)) == nil)
		goto err;
	v->startUnicodeValue = b[0]<<16 | b[1]<<8 | b[2];
	v->additionalCount = b[3];
	return 0;
err:
	werrstr("%s: %r", "UnicodeRange");
	return -1;
}

void
print_UnicodeRange(Otfile *f, int indent, Otf *o, UnicodeRange *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "startUnicodeValue", v->startUnicodeValue);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "additionalCount", v->additionalCount);
	USED(o);
}

int
read_DefaultUVS(Otf *o, DefaultUVS *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 4)) == nil)
		goto err;
	v->numUnicodeValueRanges = b[0]<<24 | b[1]<<16 | b[2]<<8 | b[3];
	if(otfarray(o, &v->ranges, read_UnicodeRange, sizeof(UnicodeRange), v->numUnicodeValueRanges) < 0){
		werrstr("%s: %r", "ranges");
		goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "DefaultUVS");
	return -1;
}

void
print_DefaultUVS(Otfile *f, int indent, Otf *o, DefaultUVS *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "numUnicodeValueRanges", v->numUnicodeValueRanges);
	for(int i = 0; i < v->numUnicodeValueRanges; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "ranges", i);
		print_UnicodeRange(f, indent+indentΔ, o, &v->ranges[i]);
	}
	USED(o);
}

int
read_UVSMapping(Otf *o, UVSMapping *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 5)) == nil)
		goto err;
	v->unicodeValue = b[0]<<16 | b[1]<<8 | b[2];
	v->glyphID = b[3]<<8 | b[4];
	return 0;
err:
	werrstr("%s: %r", "UVSMapping");
	return -1;
}

void
print_UVSMapping(Otfile *f, int indent, Otf *o, UVSMapping *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "unicodeValue", v->unicodeValue);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "glyphID", v->glyphID);
	USED(o);
}

int
read_NonDefaultUVS(Otf *o, NonDefaultUVS *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 4)) == nil)
		goto err;
	v->numUVSMappings = b[0]<<24 | b[1]<<16 | b[2]<<8 | b[3];
	if(otfarray(o, &v->uvsMappings, read_UVSMapping, sizeof(UVSMapping), v->numUVSMappings) < 0){
		werrstr("%s: %r", "uvsMappings");
		goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "NonDefaultUVS");
	return -1;
}

void
print_NonDefaultUVS(Otfile *f, int indent, Otf *o, NonDefaultUVS *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "numUVSMappings", v->numUVSMappings);
	for(int i = 0; i < v->numUVSMappings; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "uvsMappings", i);
		print_UVSMapping(f, indent+indentΔ, o, &v->uvsMappings[i]);
	}
	USED(o);
}

int
read_VariationSelector(Otf *o, VariationSelector *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 11)) == nil)
		goto err;
	v->varSelector = b[0]<<16 | b[1]<<8 | b[2];
	v->defaultUVSOffset = b[3]<<24 | b[4]<<16 | b[5]<<8 | b[6];
	v->nonDefaultUVSOffset = b[7]<<24 | b[8]<<16 | b[9]<<8 | b[10];
	if(v->defaultUVSOffset != 0){
		if(otfpushrange(o, v->defaultUVSOffset, -1) < 0)
			goto err;
		v->defaultUVS = calloc(1, sizeof(*v->defaultUVS));
		if(read_DefaultUVS(o, v->defaultUVS) < 0){
			werrstr("%s: %r", "defaultUVS");
			goto err;
		}
		if(otfpoprange(o) < 0)
			goto err;
	}
	if(v->nonDefaultUVSOffset != 0){
		if(otfpushrange(o, v->nonDefaultUVSOffset, -1) < 0)
			goto err;
		v->nonDefaultUVS = calloc(1, sizeof(*v->nonDefaultUVS));
		if(read_NonDefaultUVS(o, v->nonDefaultUVS) < 0){
			werrstr("%s: %r", "nonDefaultUVS");
			goto err;
		}
		if(otfpoprange(o) < 0)
			goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "VariationSelector");
	return -1;
}

void
print_VariationSelector(Otfile *f, int indent, Otf *o, VariationSelector *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "varSelector", v->varSelector);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "defaultUVSOffset", v->defaultUVSOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "nonDefaultUVSOffset", v->nonDefaultUVSOffset);
	f->print(f->aux, "%*s%s:\n", indent, "", "defaultUVS");
	if(v->defaultUVS != nil)
		print_DefaultUVS(f, indent+indentΔ, o, v->defaultUVS);
	f->print(f->aux, "%*s%s:\n", indent, "", "nonDefaultUVS");
	if(v->nonDefaultUVS != nil)
		print_NonDefaultUVS(f, indent+indentΔ, o, v->nonDefaultUVS);
	USED(o);
}

int
read_SubtableCmap14(Otf *o, SubtableCmap14 *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 8)) == nil)
		goto err;
	v->length = b[0]<<24 | b[1]<<16 | b[2]<<8 | b[3];
	v->numVarSelectorRecords = b[4]<<24 | b[5]<<16 | b[6]<<8 | b[7];
	if(otfarray(o, &v->varSelector, read_VariationSelector, sizeof(VariationSelector), v->numVarSelectorRecords) < 0){
		werrstr("%s: %r", "varSelector");
		goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "SubtableCmap14");
	return -1;
}

void
print_SubtableCmap14(Otfile *f, int indent, Otf *o, SubtableCmap14 *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "length", v->length);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "numVarSelectorRecords", v->numVarSelectorRecords);
	for(int i = 0; i < v->numVarSelectorRecords; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "varSelector", i);
		print_VariationSelector(f, indent+indentΔ, o, &v->varSelector[i]);
	}
	USED(o);
}

int
read_SubtableCmap(Otf *o, SubtableCmap *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 2)) == nil)
		goto err;
	v->format = b[0]<<8 | b[1];
	if(v->format != 0 && v->format != 2 && v->format != 4 && v->format != 6 && v->format != 8 && v->format != 10 && v->format != 12 && v->format != 13 && v->format != 14){
		werrstr("%s: invalid value: %d (%#ux)", "format", v->format, v->format);
		goto err;
	}
	if(v->format == 0){
		if(read_SubtableCmap0(o, &v->sub0) < 0){
			werrstr("%s: %r", "sub0");
			goto err;
		}
	}
	if(v->format == 2){
		if(read_SubtableCmap2(o, &v->sub2) < 0){
			werrstr("%s: %r", "sub2");
			goto err;
		}
	}
	if(v->format == 4){
		if(read_SubtableCmap4(o, &v->sub4) < 0){
			werrstr("%s: %r", "sub4");
			goto err;
		}
	}
	if(v->format == 6){
		if(read_SubtableCmap6(o, &v->sub6) < 0){
			werrstr("%s: %r", "sub6");
			goto err;
		}
	}
	if(v->format == 8){
		if(read_SubtableCmap8(o, &v->sub8) < 0){
			werrstr("%s: %r", "sub8");
			goto err;
		}
	}
	if(v->format == 10){
		if(read_SubtableCmap10(o, &v->sub10) < 0){
			werrstr("%s: %r", "sub10");
			goto err;
		}
	}
	if(v->format == 12 || v->format == 13){
		if(read_SubtableCmap12or13(o, &v->sub12or13) < 0){
			werrstr("%s: %r", "sub12or13");
			goto err;
		}
	}
	if(v->format == 14){
		if(read_SubtableCmap14(o, &v->sub14) < 0){
			werrstr("%s: %r", "sub14");
			goto err;
		}
	}
	return 0;
err:
	werrstr("%s: %r", "SubtableCmap");
	return -1;
}

void
print_SubtableCmap(Otfile *f, int indent, Otf *o, SubtableCmap *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "format", v->format);
	if(v->format == 0){
		f->print(f->aux, "%*s%s:\n", indent, "", "sub0");
		print_SubtableCmap0(f, indent+indentΔ, o, &v->sub0);
	}
	if(v->format == 2){
		f->print(f->aux, "%*s%s:\n", indent, "", "sub2");
		print_SubtableCmap2(f, indent+indentΔ, o, &v->sub2);
	}
	if(v->format == 4){
		f->print(f->aux, "%*s%s:\n", indent, "", "sub4");
		print_SubtableCmap4(f, indent+indentΔ, o, &v->sub4);
	}
	if(v->format == 6){
		f->print(f->aux, "%*s%s:\n", indent, "", "sub6");
		print_SubtableCmap6(f, indent+indentΔ, o, &v->sub6);
	}
	if(v->format == 8){
		f->print(f->aux, "%*s%s:\n", indent, "", "sub8");
		print_SubtableCmap8(f, indent+indentΔ, o, &v->sub8);
	}
	if(v->format == 10){
		f->print(f->aux, "%*s%s:\n", indent, "", "sub10");
		print_SubtableCmap10(f, indent+indentΔ, o, &v->sub10);
	}
	if(v->format == 12 || v->format == 13){
		f->print(f->aux, "%*s%s:\n", indent, "", "sub12or13");
		print_SubtableCmap12or13(f, indent+indentΔ, o, &v->sub12or13);
	}
	if(v->format == 14){
		f->print(f->aux, "%*s%s:\n", indent, "", "sub14");
		print_SubtableCmap14(f, indent+indentΔ, o, &v->sub14);
	}
	USED(o);
}

int
read_EncodingRecord(Otf *o, EncodingRecord *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 8)) == nil)
		goto err;
	v->platformID = b[0]<<8 | b[1];
	if(v->platformID > 4){
		werrstr("%s: invalid value: %d (%#ux)", "platformID", v->platformID, v->platformID);
		goto err;
	}
	v->encodingID = b[2]<<8 | b[3];
	v->subtableOffset = b[4]<<24 | b[5]<<16 | b[6]<<8 | b[7];
	if(v->subtableOffset != 0){
		if(otfpushrange(o, v->subtableOffset, -1) < 0)
			goto err;
		v->subtable = calloc(1, sizeof(*v->subtable));
		if(read_SubtableCmap(o, v->subtable) < 0){
			werrstr("%s: %r", "subtable");
			goto err;
		}
		if(otfpoprange(o) < 0)
			goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "EncodingRecord");
	return -1;
}

void
print_EncodingRecord(Otfile *f, int indent, Otf *o, EncodingRecord *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "platformID", v->platformID);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "encodingID", v->encodingID);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "subtableOffset", v->subtableOffset);
	f->print(f->aux, "%*s%s:\n", indent, "", "subtable");
	if(v->subtable != nil)
		print_SubtableCmap(f, indent+indentΔ, o, v->subtable);
	USED(o);
}

int
read_TableCmap(Otf *o, TableCmap *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 4)) == nil)
		goto err;
	u16int version = b[0]<<8 | b[1];
	if(version != 0){
		werrstr("%s: invalid value: %d (%#ux)", "version", version, version);
		goto err;
	}
	v->numTables = b[2]<<8 | b[3];
	if(otfarray(o, &v->encodingRecords, read_EncodingRecord, sizeof(EncodingRecord), v->numTables) < 0){
		werrstr("%s: %r", "encodingRecords");
		goto err;
	}
	if(otfcmap(v) < 0)
		goto err;
	return 0;
err:
	werrstr("%s: %r", "TableCmap");
	return -1;
}

void
print_TableCmap(Otfile *f, int indent, Otf *o, TableCmap *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "numTables", v->numTables);
	for(int i = 0; i < v->numTables; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "encodingRecords", i);
		print_EncodingRecord(f, indent+indentΔ, o, &v->encodingRecords[i]);
	}
	USED(o);
}

int
read_TableHead(Otf *o, TableHead *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 54)) == nil)
		goto err;
	u16int majorVersion = b[0]<<8 | b[1];
	if(majorVersion != 1){
		werrstr("%s: invalid value: %d (%#ux)", "majorVersion", majorVersion, majorVersion);
		goto err;
	}
	u16int minorVersion = b[2]<<8 | b[3];
	if(minorVersion != 0){
		werrstr("%s: invalid value: %d (%#ux)", "minorVersion", minorVersion, minorVersion);
		goto err;
	}
	u32int magicNumber = b[12]<<24 | b[13]<<16 | b[14]<<8 | b[15];
	if(magicNumber != 0x5f0f3cf5){
		werrstr("%s: invalid value: %d (%#ux)", "magicNumber", magicNumber, magicNumber);
		goto err;
	}
	v->flags = b[16]<<8 | b[17];
	v->unitsPerEm = b[18]<<8 | b[19];
	if(v->unitsPerEm < 16 || v->unitsPerEm > 16384){
		werrstr("%s: invalid value: %d (%#ux)", "unitsPerEm", v->unitsPerEm, v->unitsPerEm);
		goto err;
	}
	v->created = ((s64int)b[20]<<56 | (s64int)b[21]<<48 | (s64int)b[22]<<40 | (s64int)b[23]<<32 | b[24]<<24 | b[25]<<16 | b[26]<<8 | b[27]) - 2082844800LL;
	v->modified = ((s64int)b[28]<<56 | (s64int)b[29]<<48 | (s64int)b[30]<<40 | (s64int)b[31]<<32 | b[32]<<24 | b[33]<<16 | b[34]<<8 | b[35]) - 2082844800LL;
	v->xMin = b[36]<<8 | b[37];
	v->yMin = b[38]<<8 | b[39];
	v->xMax = b[40]<<8 | b[41];
	v->yMax = b[42]<<8 | b[43];
	v->macStyle = b[44]<<8 | b[45];
	v->lowestRecPPEM = b[46]<<8 | b[47];
	s16int fontDirectionHint = b[48]<<8 | b[49];
	if(fontDirectionHint < -2 || fontDirectionHint > 2){
		werrstr("%s: invalid value: %d (%#ux)", "fontDirectionHint", fontDirectionHint, fontDirectionHint);
		goto err;
	}
	v->indexToLocFormat = b[50]<<8 | b[51];
	o->indexToLocFormat = v->indexToLocFormat;
	if(v->indexToLocFormat > 1){
		werrstr("%s: invalid value: %d (%#ux)", "indexToLocFormat", v->indexToLocFormat, v->indexToLocFormat);
		goto err;
	}
	s16int glyphDataFormat = b[52]<<8 | b[53];
	if(glyphDataFormat != 0){
		werrstr("%s: invalid value: %d (%#ux)", "glyphDataFormat", glyphDataFormat, glyphDataFormat);
		goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "TableHead");
	return -1;
}

void
print_TableHead(Otfile *f, int indent, Otf *o, TableHead *v)
{
	f->print(f->aux, "%*s%s: %#ux%s%s%s%s%s%s%s%s%s\n", indent, "", "flags", v->flags, (v->flags&HEAD_FL_BASELINE_Y_0)?" HEAD_FL_BASELINE_Y_0":"", (v->flags&HEAD_FL_LEFT_SIDEBEARING_X_0)?" HEAD_FL_LEFT_SIDEBEARING_X_0":"", (v->flags&HEAD_FL_INSTR_DEP_POINT_SZ)?" HEAD_FL_INSTR_DEP_POINT_SZ":"", (v->flags&HEAD_FL_FORCE_PPEM_INT)?" HEAD_FL_FORCE_PPEM_INT":"", (v->flags&HEAD_FL_INSTR_ALT_ADVANCE_WIDTH)?" HEAD_FL_INSTR_ALT_ADVANCE_WIDTH":"", (v->flags&HEAD_FL_LOSSLESS)?" HEAD_FL_LOSSLESS":"", (v->flags&HEAD_FL_CONVERTED)?" HEAD_FL_CONVERTED":"", (v->flags&HEAD_FL_CLEARTYPE)?" HEAD_FL_CLEARTYPE":"", (v->flags&HEAD_FL_LAST_RESORT)?" HEAD_FL_LAST_RESORT":"");
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "unitsPerEm", v->unitsPerEm);
	f->print(f->aux, "%*s%s: %τ\n", indent, "", "created", fmttime(v->created));
	f->print(f->aux, "%*s%s: %τ\n", indent, "", "modified", fmttime(v->modified));
	f->print(f->aux, "%*s%s: %d\n", indent, "", "xMin", v->xMin);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "yMin", v->yMin);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "xMax", v->xMax);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "yMax", v->yMax);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "macStyle", v->macStyle);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "lowestRecPPEM", v->lowestRecPPEM);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "indexToLocFormat", v->indexToLocFormat);
	o->indexToLocFormat = v->indexToLocFormat;
	USED(o);
}

int
read_TableHhea(Otf *o, TableHhea *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 36)) == nil)
		goto err;
	v->majorVersion = b[0]<<8 | b[1];
	if(v->majorVersion != 1){
		werrstr("%s: invalid value: %d (%#ux)", "majorVersion", v->majorVersion, v->majorVersion);
		goto err;
	}
	v->minorVersion = b[2]<<8 | b[3];
	if(v->minorVersion != 0){
		werrstr("%s: invalid value: %d (%#ux)", "minorVersion", v->minorVersion, v->minorVersion);
		goto err;
	}
	v->ascender = b[4]<<8 | b[5];
	v->descender = b[6]<<8 | b[7];
	v->lineGap = b[8]<<8 | b[9];
	v->advanceWidthMax = b[10]<<8 | b[11];
	v->minLeftSideBearing = b[12]<<8 | b[13];
	v->minRightSideBearing = b[14]<<8 | b[15];
	v->xMaxExtent = b[16]<<8 | b[17];
	v->caretSlopeRise = b[18]<<8 | b[19];
	v->caretSlopeRun = b[20]<<8 | b[21];
	v->caretOffset = b[22]<<8 | b[23];
	v->metricDataFormat = b[32]<<8 | b[33];
	if(v->metricDataFormat != 0){
		werrstr("%s: invalid value: %d (%#ux)", "metricDataFormat", v->metricDataFormat, v->metricDataFormat);
		goto err;
	}
	v->numberOfHMetrics = b[34]<<8 | b[35];
	o->numberOfHMetrics = v->numberOfHMetrics;
	return 0;
err:
	werrstr("%s: %r", "TableHhea");
	return -1;
}

void
print_TableHhea(Otfile *f, int indent, Otf *o, TableHhea *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "majorVersion", v->majorVersion);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "minorVersion", v->minorVersion);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "ascender", v->ascender);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "descender", v->descender);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "lineGap", v->lineGap);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "advanceWidthMax", v->advanceWidthMax);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "minLeftSideBearing", v->minLeftSideBearing);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "minRightSideBearing", v->minRightSideBearing);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "xMaxExtent", v->xMaxExtent);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "caretSlopeRise", v->caretSlopeRise);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "caretSlopeRun", v->caretSlopeRun);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "caretOffset", v->caretOffset);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "metricDataFormat", v->metricDataFormat);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "numberOfHMetrics", v->numberOfHMetrics);
	o->numberOfHMetrics = v->numberOfHMetrics;
	USED(o);
}

int
read_EbdtComponent(Otf *o, EbdtComponent *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 4)) == nil)
		goto err;
	v->glyphID = b[0]<<8 | b[1];
	v->xOffset = b[2];
	v->yOffset = b[3];
	return 0;
err:
	werrstr("%s: %r", "EbdtComponent");
	return -1;
}

void
print_EbdtComponent(Otfile *f, int indent, Otf *o, EbdtComponent *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "glyphID", v->glyphID);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "xOffset", v->xOffset);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "yOffset", v->yOffset);
	USED(o);
}

int
read_Glyf(Otf *o, Glyf *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 10)) == nil)
		goto err;
	v->numberOfContours = b[0]<<8 | b[1];
	o->numberOfContours = v->numberOfContours;
	v->xMin = b[2]<<8 | b[3];
	v->yMin = b[4]<<8 | b[5];
	v->xMax = b[6]<<8 | b[7];
	v->yMax = b[8]<<8 | b[9];
	if(v->numberOfContours < 0){
		if(read_ComponentGlyph(o, &v->component, 0) < 0)
			goto err;
		v->type = GLYPH_COMPONENT;
	}else if(v->numberOfContours > 0){
		if(read_SimpleGlyph(o, &v->simple) < 0)
			goto err;
		v->type = GLYPH_SIMPLE;
	}else if(o->td.eblc != nil && o->td.ebdt != nil)
		v->type = GLYPH_BITMAP;
	else
		v->type = GLYPH_EMPTY;
	return 0;
err:
	werrstr("%s: %r", "Glyf");
	return -1;
}

void
print_Glyf(Otfile *f, int indent, Otf *o, Glyf *v)
{
	f->print(f->aux, "%*s%s: %d\n", indent, "", "numberOfContours", v->numberOfContours);
	o->numberOfContours = v->numberOfContours;
	f->print(f->aux, "%*s%s: %d\n", indent, "", "xMin", v->xMin);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "yMin", v->yMin);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "xMax", v->xMax);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "yMax", v->yMax);
	if(v->type == GLYPH_BITMAP){
		f->print(f->aux, "%*s%s:\n", indent, "", "bitmap");
		print_BitmapGlyph(f, indent+indentΔ, o, v->bitmap);
	}else if(v->type == GLYPH_COMPONENT){
		f->print(f->aux, "%*s%s:\n", indent, "", "component");
		print_ComponentGlyph(f, indent+indentΔ, o, v->component);
	}else if(v->type == GLYPH_SIMPLE){
		f->print(f->aux, "%*s%s:\n", indent, "", "simple");
		print_SimpleGlyph(f, indent+indentΔ, o, v->simple);
	}
	USED(o);
}

int
read_LongHorMetric(Otf *o, LongHorMetric *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 4)) == nil)
		goto err;
	v->advanceWidth = b[0]<<8 | b[1];
	v->lsb = b[2]<<8 | b[3];
	return 0;
err:
	werrstr("%s: %r", "LongHorMetric");
	return -1;
}

void
print_LongHorMetric(Otfile *f, int indent, Otf *o, LongHorMetric *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "advanceWidth", v->advanceWidth);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "lsb", v->lsb);
	USED(o);
}

int
read_TableMaxp(Otf *o, TableMaxp *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 6)) == nil)
		goto err;
	u32int version = b[0]<<24 | b[1]<<16 | b[2]<<8 | b[3];
	if(version != 20480 && version != 0x10000){
		werrstr("%s: invalid value: %d (%#ux)", "version", version, version);
		goto err;
	}
	v->numGlyphs = b[4]<<8 | b[5];
	o->numGlyphs = v->numGlyphs;
	return 0;
err:
	werrstr("%s: %r", "TableMaxp");
	return -1;
}

void
print_TableMaxp(Otfile *f, int indent, Otf *o, TableMaxp *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "numGlyphs", v->numGlyphs);
	o->numGlyphs = v->numGlyphs;
	USED(o);
}

int
read_TableHmtx(Otf *o, TableHmtx *v)
{
	u8int *b = nil; USED(b);
	if(otfarray(o, &v->hMetrics, read_LongHorMetric, sizeof(LongHorMetric), o->numberOfHMetrics) < 0){
		werrstr("%s: %r", "hMetrics");
		goto err;
	}
	if((b = otfreadn(o, (o->numGlyphs-o->numberOfHMetrics)*2)) == nil)
		goto err;
	v->leftSideBearings = malloc((o->numGlyphs-o->numberOfHMetrics)*sizeof(*v->leftSideBearings));
	for(int i = 0; i < (o->numGlyphs-o->numberOfHMetrics); i++)
		v->leftSideBearings[i] = b[0+i*2]<<8 | b[1+i*2];
	return 0;
err:
	werrstr("%s: %r", "TableHmtx");
	return -1;
}

void
print_TableHmtx(Otfile *f, int indent, Otf *o, TableHmtx *v)
{
	for(int i = 0; i < o->numberOfHMetrics; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "hMetrics", i);
		print_LongHorMetric(f, indent+indentΔ, o, &v->hMetrics[i]);
	}
	for(int i = 0; i < (o->numGlyphs-o->numberOfHMetrics); i++)
		f->print(f->aux, "%*s%s[%d]: %d\n", indent, "", "leftSideBearings", i, v->leftSideBearings[i]);
	USED(o);
}

int
read_TablePost(Otf *o, TablePost *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 16)) == nil)
		goto err;
	u32int version = b[0]<<24 | b[1]<<16 | b[2]<<8 | b[3];
	if(version != 0x10000 && version != 0x20000 && version != 0x25000 && version != 0x30000){
		werrstr("%s: invalid value: %d (%#ux)", "version", version, version);
		goto err;
	}
	v->italicAngle = (b[4]<<24 | b[5]<<16 | b[6]<<8 | b[7])/65536.0f;
	v->underlinePosition = b[8]<<8 | b[9];
	v->underlineThickness = b[10]<<8 | b[11];
	v->isFixedPitch = b[12]<<24 | b[13]<<16 | b[14]<<8 | b[15];
	return 0;
err:
	werrstr("%s: %r", "TablePost");
	return -1;
}

void
print_TablePost(Otfile *f, int indent, Otf *o, TablePost *v)
{
	f->print(f->aux, "%*s%s: %g\n", indent, "", "italicAngle", v->italicAngle);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "underlinePosition", v->underlinePosition);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "underlineThickness", v->underlineThickness);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "isFixedPitch", v->isFixedPitch);
	USED(o);
}

int
read_NameRecord(Otf *o, NameRecord *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 12)) == nil)
		goto err;
	v->platformID = b[0]<<8 | b[1];
	o->platformID = v->platformID;
	v->encodingID = b[2]<<8 | b[3];
	o->encodingID = v->encodingID;
	v->languageID = b[4]<<8 | b[5];
	v->nameID = b[6]<<8 | b[7];
	v->stringLength = b[8]<<8 | b[9];
	v->stringOffset = b[10]<<8 | b[11];
	if((o->storageOffset+v->stringOffset) != 0){
		if(otfpushrange(o, (o->storageOffset+v->stringOffset), -1) < 0)
			goto err;
		if((b = otfreadn(o, v->stringLength)) == nil)
			goto err;
		v->string = strtoutf8(o, b, v->stringLength);
		if(otfpoprange(o) < 0)
			goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "NameRecord");
	return -1;
}

void
print_NameRecord(Otfile *f, int indent, Otf *o, NameRecord *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "platformID", v->platformID);
	o->platformID = v->platformID;
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "encodingID", v->encodingID);
	o->encodingID = v->encodingID;
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "languageID", v->languageID);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "nameID", v->nameID);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "stringLength", v->stringLength);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "stringOffset", v->stringOffset);
	f->print(f->aux, "%*s%s: %s\n", indent, "", "string", v->string);
	USED(o);
}

int
read_LangTagRecord(Otf *o, LangTagRecord *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 4)) == nil)
		goto err;
	v->length = b[0]<<8 | b[1];
	v->langTagOffset = b[2]<<8 | b[3];
	if((o->storageOffset+v->langTagOffset) != 0){
		if(otfpushrange(o, (o->storageOffset+v->langTagOffset), -1) < 0)
			goto err;
		if((b = otfreadn(o, v->length)) == nil)
			goto err;
		v->langTag = malloc(v->length/2+1);
		utf16to8((u8int*)v->langTag, v->length/2+1, b, v->length);
		if(otfpoprange(o) < 0)
			goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "LangTagRecord");
	return -1;
}

void
print_LangTagRecord(Otfile *f, int indent, Otf *o, LangTagRecord *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "length", v->length);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "langTagOffset", v->langTagOffset);
	f->print(f->aux, "%*s%s: %s\n", indent, "", "langTag", v->langTag);
	USED(o);
}

int
read_TableName(Otf *o, TableName *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 6)) == nil)
		goto err;
	v->version = b[0]<<8 | b[1];
	if(v->version != 0 && v->version != 1){
		werrstr("%s: invalid value: %d (%#ux)", "version", v->version, v->version);
		goto err;
	}
	v->count = b[2]<<8 | b[3];
	v->storageOffset = b[4]<<8 | b[5];
	o->storageOffset = v->storageOffset;
	if(otfarray(o, &v->nameRecord, read_NameRecord, sizeof(NameRecord), v->count) < 0){
		werrstr("%s: %r", "nameRecord");
		goto err;
	}
	if(v->version >= 1){
		if((b = otfreadn(o, 2)) == nil)
			goto err;
		v->langTagCount = b[0]<<8 | b[1];
	}
	if(v->version >= 1){
		if(otfarray(o, &v->langTagRecord, read_LangTagRecord, sizeof(LangTagRecord), v->langTagCount) < 0){
			werrstr("%s: %r", "langTagRecord");
			goto err;
		}
	}
	return 0;
err:
	werrstr("%s: %r", "TableName");
	return -1;
}

void
print_TableName(Otfile *f, int indent, Otf *o, TableName *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "version", v->version);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "count", v->count);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "storageOffset", v->storageOffset);
	o->storageOffset = v->storageOffset;
	for(int i = 0; i < v->count; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "nameRecord", i);
		print_NameRecord(f, indent+indentΔ, o, &v->nameRecord[i]);
	}
	if(v->version >= 1)
		f->print(f->aux, "%*s%s: %ud\n", indent, "", "langTagCount", v->langTagCount);
	if(v->version >= 1){
		for(int i = 0; i < v->langTagCount; i++){
			f->print(f->aux, "%*s%s[%d]:\n", indent, "", "langTagRecord", i);
			print_LangTagRecord(f, indent+indentΔ, o, &v->langTagRecord[i]);
		}
	}
	USED(o);
}

int
read_BigGlyphMetrics(Otf *o, BigGlyphMetrics *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 8)) == nil)
		goto err;
	v->height = b[0];
	v->width = b[1];
	v->horiBearingX = b[2];
	v->horiBearingY = b[3];
	v->horiAdvance = b[4];
	v->vertBearingX = b[5];
	v->vertBearingY = b[6];
	v->vertAdvance = b[7];
	return 0;
err:
	werrstr("%s: %r", "BigGlyphMetrics");
	return -1;
}

void
print_BigGlyphMetrics(Otfile *f, int indent, Otf *o, BigGlyphMetrics *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "height", v->height);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "width", v->width);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "horiBearingX", v->horiBearingX);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "horiBearingY", v->horiBearingY);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "horiAdvance", v->horiAdvance);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "vertBearingX", v->vertBearingX);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "vertBearingY", v->vertBearingY);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "vertAdvance", v->vertAdvance);
	USED(o);
}

int
read_SmallGlyphMetrics(Otf *o, SmallGlyphMetrics *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 5)) == nil)
		goto err;
	v->height = b[0];
	v->width = b[1];
	v->bearingX = b[2];
	v->bearingY = b[3];
	v->advance = b[4];
	return 0;
err:
	werrstr("%s: %r", "SmallGlyphMetrics");
	return -1;
}

void
print_SmallGlyphMetrics(Otfile *f, int indent, Otf *o, SmallGlyphMetrics *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "height", v->height);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "width", v->width);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "bearingX", v->bearingX);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "bearingY", v->bearingY);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "advance", v->advance);
	USED(o);
}

int
read_SbitLineMetrics(Otf *o, SbitLineMetrics *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 12)) == nil)
		goto err;
	v->ascender = b[0];
	v->descender = b[1];
	v->widthMax = b[2];
	v->caretSlopeNumerator = b[3];
	v->caretSlopeDenumerator = b[4];
	v->caretOffset = b[5];
	v->minOriginSB = b[6];
	v->minAdvanceSB = b[7];
	v->maxBeforeBL = b[8];
	v->minAfterBL = b[9];
	return 0;
err:
	werrstr("%s: %r", "SbitLineMetrics");
	return -1;
}

void
print_SbitLineMetrics(Otfile *f, int indent, Otf *o, SbitLineMetrics *v)
{
	f->print(f->aux, "%*s%s: %d\n", indent, "", "ascender", v->ascender);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "descender", v->descender);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "widthMax", v->widthMax);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "caretSlopeNumerator", v->caretSlopeNumerator);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "caretSlopeDenumerator", v->caretSlopeDenumerator);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "caretOffset", v->caretOffset);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "minOriginSB", v->minOriginSB);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "minAdvanceSB", v->minAdvanceSB);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "maxBeforeBL", v->maxBeforeBL);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "minAfterBL", v->minAfterBL);
	USED(o);
}

int
read_IndexSubtable1(Otf *o, IndexSubtable1 *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, ((o->lastGlyphIndex-o->firstGlyphIndex)+2)*4)) == nil)
		goto err;
	v->sbitOffsets = malloc(((o->lastGlyphIndex-o->firstGlyphIndex)+2)*sizeof(*v->sbitOffsets));
	for(int i = 0; i < ((o->lastGlyphIndex-o->firstGlyphIndex)+2); i++)
		v->sbitOffsets[i] = b[0+i*4]<<24 | b[1+i*4]<<16 | b[2+i*4]<<8 | b[3+i*4];
	return 0;
err:
	werrstr("%s: %r", "IndexSubtable1");
	return -1;
}

void
print_IndexSubtable1(Otfile *f, int indent, Otf *o, IndexSubtable1 *v)
{
	for(int i = 0; i < ((o->lastGlyphIndex-o->firstGlyphIndex)+2); i++)
		f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "sbitOffsets", i, v->sbitOffsets[i]);
	USED(o);
}

int
read_IndexSubtable2(Otf *o, IndexSubtable2 *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 4)) == nil)
		goto err;
	v->imageSize = b[0]<<24 | b[1]<<16 | b[2]<<8 | b[3];
	if(read_BigGlyphMetrics(o, &v->bigMetrics) < 0){
		werrstr("%s: %r", "bigMetrics");
		goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "IndexSubtable2");
	return -1;
}

void
print_IndexSubtable2(Otfile *f, int indent, Otf *o, IndexSubtable2 *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "imageSize", v->imageSize);
	f->print(f->aux, "%*s%s:\n", indent, "", "bigMetrics");
	print_BigGlyphMetrics(f, indent+indentΔ, o, &v->bigMetrics);
	USED(o);
}

int
read_IndexSubtable3(Otf *o, IndexSubtable3 *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, ((o->lastGlyphIndex-o->firstGlyphIndex)+2)*2)) == nil)
		goto err;
	v->sbitOffsets = malloc(((o->lastGlyphIndex-o->firstGlyphIndex)+2)*sizeof(*v->sbitOffsets));
	for(int i = 0; i < ((o->lastGlyphIndex-o->firstGlyphIndex)+2); i++)
		v->sbitOffsets[i] = b[0+i*2]<<8 | b[1+i*2];
	return 0;
err:
	werrstr("%s: %r", "IndexSubtable3");
	return -1;
}

void
print_IndexSubtable3(Otfile *f, int indent, Otf *o, IndexSubtable3 *v)
{
	for(int i = 0; i < ((o->lastGlyphIndex-o->firstGlyphIndex)+2); i++)
		f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "sbitOffsets", i, v->sbitOffsets[i]);
	USED(o);
}

int
read_GlyphIdOffsetPair(Otf *o, GlyphIdOffsetPair *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 4)) == nil)
		goto err;
	v->glyphID = b[0]<<8 | b[1];
	v->sbitOffset = b[2]<<8 | b[3];
	return 0;
err:
	werrstr("%s: %r", "GlyphIdOffsetPair");
	return -1;
}

void
print_GlyphIdOffsetPair(Otfile *f, int indent, Otf *o, GlyphIdOffsetPair *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "glyphID", v->glyphID);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "sbitOffset", v->sbitOffset);
	USED(o);
}

int
read_IndexSubtable4(Otf *o, IndexSubtable4 *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 4)) == nil)
		goto err;
	v->numGlyphs = b[0]<<24 | b[1]<<16 | b[2]<<8 | b[3];
	if(otfarray(o, &v->glyphArray, read_GlyphIdOffsetPair, sizeof(GlyphIdOffsetPair), (v->numGlyphs+1)) < 0){
		werrstr("%s: %r", "glyphArray");
		goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "IndexSubtable4");
	return -1;
}

void
print_IndexSubtable4(Otfile *f, int indent, Otf *o, IndexSubtable4 *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "numGlyphs", v->numGlyphs);
	for(int i = 0; i < (v->numGlyphs+1); i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "glyphArray", i);
		print_GlyphIdOffsetPair(f, indent+indentΔ, o, &v->glyphArray[i]);
	}
	USED(o);
}

int
read_IndexSubtable5(Otf *o, IndexSubtable5 *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 4)) == nil)
		goto err;
	v->imageSize = b[0]<<24 | b[1]<<16 | b[2]<<8 | b[3];
	if(read_BigGlyphMetrics(o, &v->bigMetrics) < 0){
		werrstr("%s: %r", "bigMetrics");
		goto err;
	}
	if((b = otfreadn(o, 4)) == nil)
		goto err;
	v->numGlyphs = b[0]<<24 | b[1]<<16 | b[2]<<8 | b[3];
	if((b = otfreadn(o, v->numGlyphs*2)) == nil)
		goto err;
	v->glyphIdArray = malloc(v->numGlyphs*sizeof(*v->glyphIdArray));
	for(int i = 0; i < v->numGlyphs; i++)
		v->glyphIdArray[i] = b[0+i*2]<<8 | b[1+i*2];
	return 0;
err:
	werrstr("%s: %r", "IndexSubtable5");
	return -1;
}

void
print_IndexSubtable5(Otfile *f, int indent, Otf *o, IndexSubtable5 *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "imageSize", v->imageSize);
	f->print(f->aux, "%*s%s:\n", indent, "", "bigMetrics");
	print_BigGlyphMetrics(f, indent+indentΔ, o, &v->bigMetrics);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "numGlyphs", v->numGlyphs);
	for(int i = 0; i < v->numGlyphs; i++)
		f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "glyphIdArray", i, v->glyphIdArray[i]);
	USED(o);
}

int
read_IndexSubtable(Otf *o, IndexSubtable *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 8)) == nil)
		goto err;
	v->indexFormat = b[0]<<8 | b[1];
	if(v->indexFormat < 1 || v->indexFormat > 5){
		werrstr("%s: invalid value: %d (%#ux)", "indexFormat", v->indexFormat, v->indexFormat);
		goto err;
	}
	v->imageFormat = b[2]<<8 | b[3];
	v->imageDataOffset = b[4]<<24 | b[5]<<16 | b[6]<<8 | b[7];
	if(v->indexFormat == 1){
		if(read_IndexSubtable1(o, &v->sub1) < 0){
			werrstr("%s: %r", "sub1");
			goto err;
		}
	}
	if(v->indexFormat == 2){
		if(read_IndexSubtable2(o, &v->sub2) < 0){
			werrstr("%s: %r", "sub2");
			goto err;
		}
	}
	if(v->indexFormat == 3){
		if(read_IndexSubtable3(o, &v->sub3) < 0){
			werrstr("%s: %r", "sub3");
			goto err;
		}
	}
	if(v->indexFormat == 4){
		if(read_IndexSubtable4(o, &v->sub4) < 0){
			werrstr("%s: %r", "sub4");
			goto err;
		}
	}
	if(v->indexFormat == 5){
		if(read_IndexSubtable5(o, &v->sub5) < 0){
			werrstr("%s: %r", "sub5");
			goto err;
		}
	}
	return 0;
err:
	werrstr("%s: %r", "IndexSubtable");
	return -1;
}

void
print_IndexSubtable(Otfile *f, int indent, Otf *o, IndexSubtable *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "indexFormat", v->indexFormat);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "imageFormat", v->imageFormat);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "imageDataOffset", v->imageDataOffset);
	if(v->indexFormat == 1){
		f->print(f->aux, "%*s%s:\n", indent, "", "sub1");
		print_IndexSubtable1(f, indent+indentΔ, o, &v->sub1);
	}
	if(v->indexFormat == 2){
		f->print(f->aux, "%*s%s:\n", indent, "", "sub2");
		print_IndexSubtable2(f, indent+indentΔ, o, &v->sub2);
	}
	if(v->indexFormat == 3){
		f->print(f->aux, "%*s%s:\n", indent, "", "sub3");
		print_IndexSubtable3(f, indent+indentΔ, o, &v->sub3);
	}
	if(v->indexFormat == 4){
		f->print(f->aux, "%*s%s:\n", indent, "", "sub4");
		print_IndexSubtable4(f, indent+indentΔ, o, &v->sub4);
	}
	if(v->indexFormat == 5){
		f->print(f->aux, "%*s%s:\n", indent, "", "sub5");
		print_IndexSubtable5(f, indent+indentΔ, o, &v->sub5);
	}
	USED(o);
}

int
read_IndexSubtableRecord(Otf *o, IndexSubtableRecord *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 8)) == nil)
		goto err;
	v->firstGlyphIndex = b[0]<<8 | b[1];
	o->firstGlyphIndex = v->firstGlyphIndex;
	v->lastGlyphIndex = b[2]<<8 | b[3];
	o->lastGlyphIndex = v->lastGlyphIndex;
	v->indexSubtableOffset = b[4]<<24 | b[5]<<16 | b[6]<<8 | b[7];
	if(v->indexSubtableOffset != 0){
		if(otfpushrange(o, v->indexSubtableOffset, -1) < 0)
			goto err;
		v->indexSubtable = calloc(1, sizeof(*v->indexSubtable));
		if(read_IndexSubtable(o, v->indexSubtable) < 0){
			werrstr("%s: %r", "indexSubtable");
			goto err;
		}
		if(otfpoprange(o) < 0)
			goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "IndexSubtableRecord");
	return -1;
}

void
print_IndexSubtableRecord(Otfile *f, int indent, Otf *o, IndexSubtableRecord *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "firstGlyphIndex", v->firstGlyphIndex);
	o->firstGlyphIndex = v->firstGlyphIndex;
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "lastGlyphIndex", v->lastGlyphIndex);
	o->lastGlyphIndex = v->lastGlyphIndex;
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "indexSubtableOffset", v->indexSubtableOffset);
	f->print(f->aux, "%*s%s:\n", indent, "", "indexSubtable");
	if(v->indexSubtable != nil)
		print_IndexSubtable(f, indent+indentΔ, o, v->indexSubtable);
	USED(o);
}

int
read_BitmapSize(Otf *o, BitmapSize *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 16)) == nil)
		goto err;
	v->indexSubtableListOffset = b[0]<<24 | b[1]<<16 | b[2]<<8 | b[3];
	v->indexSubtableListSize = b[4]<<24 | b[5]<<16 | b[6]<<8 | b[7];
	v->numberOfIndexSubtables = b[8]<<24 | b[9]<<16 | b[10]<<8 | b[11];
	if(read_SbitLineMetrics(o, &v->hori) < 0){
		werrstr("%s: %r", "hori");
		goto err;
	}
	if(read_SbitLineMetrics(o, &v->vert) < 0){
		werrstr("%s: %r", "vert");
		goto err;
	}
	if((b = otfreadn(o, 8)) == nil)
		goto err;
	v->startGlyphIndex = b[0]<<8 | b[1];
	v->endGlyphIndex = b[2]<<8 | b[3];
	v->ppemX = b[4];
	v->ppemY = b[5];
	v->bitDepth = b[6];
	if(v->bitDepth != 1 && v->bitDepth != 2 && v->bitDepth != 4 && v->bitDepth != 8){
		werrstr("%s: invalid value: %d (%#ux)", "bitDepth", v->bitDepth, v->bitDepth);
		goto err;
	}
	v->flags = b[7];
	if(v->indexSubtableListOffset != 0){
		if(otfpushrange(o, v->indexSubtableListOffset, -1) < 0)
			goto err;
		if(otfarray(o, &v->indexSubtableList, read_IndexSubtableRecord, sizeof(IndexSubtableRecord), v->numberOfIndexSubtables) < 0){
			werrstr("%s: %r", "indexSubtableList");
			goto err;
		}
		if(otfpoprange(o) < 0)
			goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "BitmapSize");
	return -1;
}

void
print_BitmapSize(Otfile *f, int indent, Otf *o, BitmapSize *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "indexSubtableListOffset", v->indexSubtableListOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "indexSubtableListSize", v->indexSubtableListSize);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "numberOfIndexSubtables", v->numberOfIndexSubtables);
	f->print(f->aux, "%*s%s:\n", indent, "", "hori");
	print_SbitLineMetrics(f, indent+indentΔ, o, &v->hori);
	f->print(f->aux, "%*s%s:\n", indent, "", "vert");
	print_SbitLineMetrics(f, indent+indentΔ, o, &v->vert);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "startGlyphIndex", v->startGlyphIndex);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "endGlyphIndex", v->endGlyphIndex);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "ppemX", v->ppemX);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "ppemY", v->ppemY);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "bitDepth", v->bitDepth);
	f->print(f->aux, "%*s%s: %#ux%s%s\n", indent, "", "flags", v->flags, (v->flags&BITMAPSIZE_FL_HORIZONTAL_METRICS)?" BITMAPSIZE_FL_HORIZONTAL_METRICS":"", (v->flags&BITMAPSIZE_FL_VERTICAL_METRICS)?" BITMAPSIZE_FL_VERTICAL_METRICS":"");
	for(int i = 0; i < v->numberOfIndexSubtables; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "indexSubtableList", i);
		print_IndexSubtableRecord(f, indent+indentΔ, o, &v->indexSubtableList[i]);
	}
	USED(o);
}

int
read_TableEBDT(Otf *o, TableEBDT *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 4)) == nil)
		goto err;
	v->majorVersion = b[0]<<8 | b[1];
	if(v->majorVersion != 2){
		werrstr("%s: invalid value: %d (%#ux)", "majorVersion", v->majorVersion, v->majorVersion);
		goto err;
	}
	u16int minorVersion = b[2]<<8 | b[3];
	if(minorVersion != 0){
		werrstr("%s: invalid value: %d (%#ux)", "minorVersion", minorVersion, minorVersion);
		goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "TableEBDT");
	return -1;
}

void
print_TableEBDT(Otfile *f, int indent, Otf *o, TableEBDT *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "majorVersion", v->majorVersion);
	USED(o);
}

int
read_TableEBLC(Otf *o, TableEBLC *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 8)) == nil)
		goto err;
	u16int majorVersion = b[0]<<8 | b[1];
	if(majorVersion != 2){
		werrstr("%s: invalid value: %d (%#ux)", "majorVersion", majorVersion, majorVersion);
		goto err;
	}
	u16int minorVersion = b[2]<<8 | b[3];
	if(minorVersion != 0){
		werrstr("%s: invalid value: %d (%#ux)", "minorVersion", minorVersion, minorVersion);
		goto err;
	}
	v->numSizes = b[4]<<24 | b[5]<<16 | b[6]<<8 | b[7];
	if(otfarray(o, &v->bitmapSizes, read_BitmapSize, sizeof(BitmapSize), v->numSizes) < 0){
		werrstr("%s: %r", "bitmapSizes");
		goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "TableEBLC");
	return -1;
}

void
print_TableEBLC(Otfile *f, int indent, Otf *o, TableEBLC *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "numSizes", v->numSizes);
	for(int i = 0; i < v->numSizes; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "bitmapSizes", i);
		print_BitmapSize(f, indent+indentΔ, o, &v->bitmapSizes[i]);
	}
	USED(o);
}

int
read_AttachList(Otf *o, AttachList *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 4)) == nil)
		goto err;
	v->coverageOffset = b[0]<<8 | b[1];
	v->glyphCount = b[2]<<8 | b[3];
	if((b = otfreadn(o, v->glyphCount*2)) == nil)
		goto err;
	v->attachPointOffsets = malloc(v->glyphCount*sizeof(*v->attachPointOffsets));
	for(int i = 0; i < v->glyphCount; i++)
		v->attachPointOffsets[i] = b[0+i*2]<<8 | b[1+i*2];
	return 0;
err:
	werrstr("%s: %r", "AttachList");
	return -1;
}

void
print_AttachList(Otfile *f, int indent, Otf *o, AttachList *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "coverageOffset", v->coverageOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "glyphCount", v->glyphCount);
	for(int i = 0; i < v->glyphCount; i++)
		f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "attachPointOffsets", i, v->attachPointOffsets[i]);
	USED(o);
}

int
read_AttachPoint(Otf *o, AttachPoint *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 2)) == nil)
		goto err;
	v->pointCount = b[0]<<8 | b[1];
	if((b = otfreadn(o, v->pointCount*2)) == nil)
		goto err;
	v->pointIndices = malloc(v->pointCount*sizeof(*v->pointIndices));
	for(int i = 0; i < v->pointCount; i++)
		v->pointIndices[i] = b[0+i*2]<<8 | b[1+i*2];
	return 0;
err:
	werrstr("%s: %r", "AttachPoint");
	return -1;
}

void
print_AttachPoint(Otfile *f, int indent, Otf *o, AttachPoint *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "pointCount", v->pointCount);
	for(int i = 0; i < v->pointCount; i++)
		f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "pointIndices", i, v->pointIndices[i]);
	USED(o);
}

int
read_LigCaretList(Otf *o, LigCaretList *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 4)) == nil)
		goto err;
	v->coverageOffset = b[0]<<8 | b[1];
	v->ligGlyphCount = b[2]<<8 | b[3];
	if((b = otfreadn(o, v->ligGlyphCount*2)) == nil)
		goto err;
	v->ligGlyphOffsets = malloc(v->ligGlyphCount*sizeof(*v->ligGlyphOffsets));
	for(int i = 0; i < v->ligGlyphCount; i++)
		v->ligGlyphOffsets[i] = b[0+i*2]<<8 | b[1+i*2];
	return 0;
err:
	werrstr("%s: %r", "LigCaretList");
	return -1;
}

void
print_LigCaretList(Otfile *f, int indent, Otf *o, LigCaretList *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "coverageOffset", v->coverageOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "ligGlyphCount", v->ligGlyphCount);
	for(int i = 0; i < v->ligGlyphCount; i++)
		f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "ligGlyphOffsets", i, v->ligGlyphOffsets[i]);
	USED(o);
}

int
read_LigGlyph(Otf *o, LigGlyph *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 2)) == nil)
		goto err;
	v->caretCount = b[0]<<8 | b[1];
	if((b = otfreadn(o, v->caretCount*2)) == nil)
		goto err;
	v->caretValueOffsets = malloc(v->caretCount*sizeof(*v->caretValueOffsets));
	for(int i = 0; i < v->caretCount; i++)
		v->caretValueOffsets[i] = b[0+i*2]<<8 | b[1+i*2];
	return 0;
err:
	werrstr("%s: %r", "LigGlyph");
	return -1;
}

void
print_LigGlyph(Otfile *f, int indent, Otf *o, LigGlyph *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "caretCount", v->caretCount);
	for(int i = 0; i < v->caretCount; i++)
		f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "caretValueOffsets", i, v->caretValueOffsets[i]);
	USED(o);
}

int
read_CaretValue(Otf *o, CaretValue *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 2)) == nil)
		goto err;
	v->format = b[0]<<8 | b[1];
	if(v->format < 1 || v->format > 3){
		werrstr("%s: invalid value: %d (%#ux)", "format", v->format, v->format);
		goto err;
	}
	if(v->format == 1 || v->format == 3){
		if((b = otfreadn(o, 2)) == nil)
			goto err;
		v->coordinate = b[0]<<8 | b[1];
	}
	if(v->format == 2){
		if((b = otfreadn(o, 2)) == nil)
			goto err;
		v->caretValuePointIndex = b[0]<<8 | b[1];
	}
	if(v->format == 3){
		if((b = otfreadn(o, 2)) == nil)
			goto err;
		v->deviceOffset = b[0]<<8 | b[1];
	}
	return 0;
err:
	werrstr("%s: %r", "CaretValue");
	return -1;
}

void
print_CaretValue(Otfile *f, int indent, Otf *o, CaretValue *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "format", v->format);
	if(v->format == 1 || v->format == 3)
		f->print(f->aux, "%*s%s: %d\n", indent, "", "coordinate", v->coordinate);
	if(v->format == 2)
		f->print(f->aux, "%*s%s: %ud\n", indent, "", "caretValuePointIndex", v->caretValuePointIndex);
	if(v->format == 3)
		f->print(f->aux, "%*s%s: %ud\n", indent, "", "deviceOffset", v->deviceOffset);
	USED(o);
}

int
read_ValueRecord(Otf *o, ValueRecord *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 16)) == nil)
		goto err;
	v->xPlacement = b[0]<<8 | b[1];
	v->yPlacement = b[2]<<8 | b[3];
	v->xAdvance = b[4]<<8 | b[5];
	v->yAdvance = b[6]<<8 | b[7];
	v->xPlaDeviceOffset = b[8]<<8 | b[9];
	v->yPlaDeviceOffset = b[10]<<8 | b[11];
	v->xAdvDeviceOffset = b[12]<<8 | b[13];
	v->yAdvDeviceOffset = b[14]<<8 | b[15];
	return 0;
err:
	werrstr("%s: %r", "ValueRecord");
	return -1;
}

void
print_ValueRecord(Otfile *f, int indent, Otf *o, ValueRecord *v)
{
	f->print(f->aux, "%*s%s: %d\n", indent, "", "xPlacement", v->xPlacement);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "yPlacement", v->yPlacement);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "xAdvance", v->xAdvance);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "yAdvance", v->yAdvance);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "xPlaDeviceOffset", v->xPlaDeviceOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "yPlaDeviceOffset", v->yPlaDeviceOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "xAdvDeviceOffset", v->xAdvDeviceOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "yAdvDeviceOffset", v->yAdvDeviceOffset);
	USED(o);
}

int
read_SinglePos(Otf *o, SinglePos *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 6)) == nil)
		goto err;
	v->format = b[0]<<8 | b[1];
	if(v->format != 1 && v->format != 2){
		werrstr("%s: invalid value: %d (%#ux)", "format", v->format, v->format);
		goto err;
	}
	v->coverageOffset = b[2]<<8 | b[3];
	v->valueFormat = b[4]<<8 | b[5];
	if(v->format == 1){
		if(read_ValueRecord(o, &v->valueRecord) < 0){
			werrstr("%s: %r", "valueRecord");
			goto err;
		}
	}
	if(v->format == 2){
		if((b = otfreadn(o, 2)) == nil)
			goto err;
		v->valueCount = b[0]<<8 | b[1];
	}
	if(v->format == 2){
		if(otfarray(o, &v->valueRecords, read_ValueRecord, sizeof(ValueRecord), v->valueCount) < 0){
			werrstr("%s: %r", "valueRecords");
			goto err;
		}
	}
	return 0;
err:
	werrstr("%s: %r", "SinglePos");
	return -1;
}

void
print_SinglePos(Otfile *f, int indent, Otf *o, SinglePos *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "format", v->format);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "coverageOffset", v->coverageOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "valueFormat", v->valueFormat);
	if(v->format == 1){
		f->print(f->aux, "%*s%s:\n", indent, "", "valueRecord");
		print_ValueRecord(f, indent+indentΔ, o, &v->valueRecord);
	}
	if(v->format == 2)
		f->print(f->aux, "%*s%s: %ud\n", indent, "", "valueCount", v->valueCount);
	if(v->format == 2){
		for(int i = 0; i < v->valueCount; i++){
			f->print(f->aux, "%*s%s[%d]:\n", indent, "", "valueRecords", i);
			print_ValueRecord(f, indent+indentΔ, o, &v->valueRecords[i]);
		}
	}
	USED(o);
}

int
read_TableGDEF(Otf *o, TableGDEF *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 12)) == nil)
		goto err;
	u16int majorVersion = b[0]<<8 | b[1];
	if(majorVersion != 1){
		werrstr("%s: invalid value: %d (%#ux)", "majorVersion", majorVersion, majorVersion);
		goto err;
	}
	v->minorVersion = b[2]<<8 | b[3];
	if(v->minorVersion != 0 && v->minorVersion != 2 && v->minorVersion != 3){
		werrstr("%s: invalid value: %d (%#ux)", "minorVersion", v->minorVersion, v->minorVersion);
		goto err;
	}
	v->glyphClassDefOffset = b[4]<<8 | b[5];
	v->attachListOffset = b[6]<<8 | b[7];
	v->ligCaretListOffset = b[8]<<8 | b[9];
	v->markAttachClassDefOffset = b[10]<<8 | b[11];
	if(v->minorVersion >= 2){
		if((b = otfreadn(o, 2)) == nil)
			goto err;
		v->markGlyphSetsDefOffset = b[0]<<8 | b[1];
	}
	if(v->minorVersion >= 3){
		if((b = otfreadn(o, 4)) == nil)
			goto err;
		v->itemVarStoreOffset = b[0]<<24 | b[1]<<16 | b[2]<<8 | b[3];
	}
	return 0;
err:
	werrstr("%s: %r", "TableGDEF");
	return -1;
}

void
print_TableGDEF(Otfile *f, int indent, Otf *o, TableGDEF *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "minorVersion", v->minorVersion);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "glyphClassDefOffset", v->glyphClassDefOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "attachListOffset", v->attachListOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "ligCaretListOffset", v->ligCaretListOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "markAttachClassDefOffset", v->markAttachClassDefOffset);
	if(v->minorVersion >= 2)
		f->print(f->aux, "%*s%s: %ud\n", indent, "", "markGlyphSetsDefOffset", v->markGlyphSetsDefOffset);
	if(v->minorVersion >= 3)
		f->print(f->aux, "%*s%s: %ud\n", indent, "", "itemVarStoreOffset", v->itemVarStoreOffset);
	USED(o);
}

int
read_LangSys(Otf *o, LangSys *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 6)) == nil)
		goto err;
	v->requiredFeatureIndex = b[2]<<8 | b[3];
	v->featureIndexCount = b[4]<<8 | b[5];
	if((b = otfreadn(o, v->featureIndexCount*2)) == nil)
		goto err;
	v->featureIndices = malloc(v->featureIndexCount*sizeof(*v->featureIndices));
	for(int i = 0; i < v->featureIndexCount; i++)
		v->featureIndices[i] = b[0+i*2]<<8 | b[1+i*2];
	return 0;
err:
	werrstr("%s: %r", "LangSys");
	return -1;
}

void
print_LangSys(Otfile *f, int indent, Otf *o, LangSys *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "requiredFeatureIndex", v->requiredFeatureIndex);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "featureIndexCount", v->featureIndexCount);
	for(int i = 0; i < v->featureIndexCount; i++)
		f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "featureIndices", i, v->featureIndices[i]);
	USED(o);
}

int
read_LangSysRecord(Otf *o, LangSysRecord *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 6)) == nil)
		goto err;
	v->langSysTag = b[0]<<24 | b[1]<<16 | b[2]<<8 | b[3];
	v->langSysOffset = b[4]<<8 | b[5];
	if(v->langSysOffset != 0){
		if(otfpushrange(o, v->langSysOffset, -1) < 0)
			goto err;
		v->langSys = calloc(1, sizeof(*v->langSys));
		if(read_LangSys(o, v->langSys) < 0){
			werrstr("%s: %r", "langSys");
			goto err;
		}
		if(otfpoprange(o) < 0)
			goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "LangSysRecord");
	return -1;
}

void
print_LangSysRecord(Otfile *f, int indent, Otf *o, LangSysRecord *v)
{
	f->print(f->aux, "%*s%s: %c%c%c%c\n", indent, "", "langSysTag", v->langSysTag>>24, v->langSysTag>>16, v->langSysTag>>8, v->langSysTag>>0);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "langSysOffset", v->langSysOffset);
	f->print(f->aux, "%*s%s:\n", indent, "", "langSys");
	if(v->langSys != nil)
		print_LangSys(f, indent+indentΔ, o, v->langSys);
	USED(o);
}

int
read_Script(Otf *o, Script *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 4)) == nil)
		goto err;
	v->defaultLangSysOffset = b[0]<<8 | b[1];
	v->langSysCount = b[2]<<8 | b[3];
	if(otfarray(o, &v->langSysRecords, read_LangSysRecord, sizeof(LangSysRecord), v->langSysCount) < 0){
		werrstr("%s: %r", "langSysRecords");
		goto err;
	}
	if(v->defaultLangSysOffset != 0){
		if(otfpushrange(o, v->defaultLangSysOffset, -1) < 0)
			goto err;
		v->defaultLangSys = calloc(1, sizeof(*v->defaultLangSys));
		if(read_LangSys(o, v->defaultLangSys) < 0){
			werrstr("%s: %r", "defaultLangSys");
			goto err;
		}
		if(otfpoprange(o) < 0)
			goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "Script");
	return -1;
}

void
print_Script(Otfile *f, int indent, Otf *o, Script *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "defaultLangSysOffset", v->defaultLangSysOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "langSysCount", v->langSysCount);
	for(int i = 0; i < v->langSysCount; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "langSysRecords", i);
		print_LangSysRecord(f, indent+indentΔ, o, &v->langSysRecords[i]);
	}
	f->print(f->aux, "%*s%s:\n", indent, "", "defaultLangSys");
	if(v->defaultLangSys != nil)
		print_LangSys(f, indent+indentΔ, o, v->defaultLangSys);
	USED(o);
}

int
read_ScriptRecord(Otf *o, ScriptRecord *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 6)) == nil)
		goto err;
	v->scriptTag = b[0]<<24 | b[1]<<16 | b[2]<<8 | b[3];
	v->scriptOffset = b[4]<<8 | b[5];
	if(v->scriptOffset != 0){
		if(otfpushrange(o, v->scriptOffset, -1) < 0)
			goto err;
		v->script = calloc(1, sizeof(*v->script));
		if(read_Script(o, v->script) < 0){
			werrstr("%s: %r", "script");
			goto err;
		}
		if(otfpoprange(o) < 0)
			goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "ScriptRecord");
	return -1;
}

void
print_ScriptRecord(Otfile *f, int indent, Otf *o, ScriptRecord *v)
{
	f->print(f->aux, "%*s%s: %c%c%c%c\n", indent, "", "scriptTag", v->scriptTag>>24, v->scriptTag>>16, v->scriptTag>>8, v->scriptTag>>0);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "scriptOffset", v->scriptOffset);
	f->print(f->aux, "%*s%s:\n", indent, "", "script");
	if(v->script != nil)
		print_Script(f, indent+indentΔ, o, v->script);
	USED(o);
}

int
read_ScriptList(Otf *o, ScriptList *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 2)) == nil)
		goto err;
	v->scriptCount = b[0]<<8 | b[1];
	if(otfarray(o, &v->scriptRecords, read_ScriptRecord, sizeof(ScriptRecord), v->scriptCount) < 0){
		werrstr("%s: %r", "scriptRecords");
		goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "ScriptList");
	return -1;
}

void
print_ScriptList(Otfile *f, int indent, Otf *o, ScriptList *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "scriptCount", v->scriptCount);
	for(int i = 0; i < v->scriptCount; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "scriptRecords", i);
		print_ScriptRecord(f, indent+indentΔ, o, &v->scriptRecords[i]);
	}
	USED(o);
}

int
read_Feature(Otf *o, Feature *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 4)) == nil)
		goto err;
	v->featureParamsOffset = b[0]<<8 | b[1];
	v->lookupIndexCount = b[2]<<8 | b[3];
	if((b = otfreadn(o, v->lookupIndexCount*2)) == nil)
		goto err;
	v->lookupListIndices = malloc(v->lookupIndexCount*sizeof(*v->lookupListIndices));
	for(int i = 0; i < v->lookupIndexCount; i++)
		v->lookupListIndices[i] = b[0+i*2]<<8 | b[1+i*2];
	return 0;
err:
	werrstr("%s: %r", "Feature");
	return -1;
}

void
print_Feature(Otfile *f, int indent, Otf *o, Feature *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "featureParamsOffset", v->featureParamsOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "lookupIndexCount", v->lookupIndexCount);
	for(int i = 0; i < v->lookupIndexCount; i++)
		f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "lookupListIndices", i, v->lookupListIndices[i]);
	USED(o);
}

int
read_FeatureRecord(Otf *o, FeatureRecord *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 6)) == nil)
		goto err;
	v->featureTag = b[0]<<24 | b[1]<<16 | b[2]<<8 | b[3];
	v->featureOffset = b[4]<<8 | b[5];
	if(v->featureOffset != 0){
		if(otfpushrange(o, v->featureOffset, -1) < 0)
			goto err;
		v->feature = calloc(1, sizeof(*v->feature));
		if(read_Feature(o, v->feature) < 0){
			werrstr("%s: %r", "feature");
			goto err;
		}
		if(otfpoprange(o) < 0)
			goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "FeatureRecord");
	return -1;
}

void
print_FeatureRecord(Otfile *f, int indent, Otf *o, FeatureRecord *v)
{
	f->print(f->aux, "%*s%s: %c%c%c%c\n", indent, "", "featureTag", v->featureTag>>24, v->featureTag>>16, v->featureTag>>8, v->featureTag>>0);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "featureOffset", v->featureOffset);
	f->print(f->aux, "%*s%s:\n", indent, "", "feature");
	if(v->feature != nil)
		print_Feature(f, indent+indentΔ, o, v->feature);
	USED(o);
}

int
read_FeatureList(Otf *o, FeatureList *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 2)) == nil)
		goto err;
	v->featureCount = b[0]<<8 | b[1];
	if(otfarray(o, &v->featureRecords, read_FeatureRecord, sizeof(FeatureRecord), v->featureCount) < 0){
		werrstr("%s: %r", "featureRecords");
		goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "FeatureList");
	return -1;
}

void
print_FeatureList(Otfile *f, int indent, Otf *o, FeatureList *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "featureCount", v->featureCount);
	for(int i = 0; i < v->featureCount; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "featureRecords", i);
		print_FeatureRecord(f, indent+indentΔ, o, &v->featureRecords[i]);
	}
	USED(o);
}

int
read_SequenceLookup(Otf *o, SequenceLookup *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 4)) == nil)
		goto err;
	v->sequenceIndex = b[0]<<8 | b[1];
	v->lookupListIndex = b[2]<<8 | b[3];
	return 0;
err:
	werrstr("%s: %r", "SequenceLookup");
	return -1;
}

void
print_SequenceLookup(Otfile *f, int indent, Otf *o, SequenceLookup *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "sequenceIndex", v->sequenceIndex);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "lookupListIndex", v->lookupListIndex);
	USED(o);
}

int
read_ChainedSequenceRule(Otf *o, ChainedSequenceRule *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 2)) == nil)
		goto err;
	v->backtrackGlyphCount = b[0]<<8 | b[1];
	if((b = otfreadn(o, v->backtrackGlyphCount*2)) == nil)
		goto err;
	v->backtrackSequence = malloc(v->backtrackGlyphCount*sizeof(*v->backtrackSequence));
	for(int i = 0; i < v->backtrackGlyphCount; i++)
		v->backtrackSequence[i] = b[0+i*2]<<8 | b[1+i*2];
	if((b = otfreadn(o, 2)) == nil)
		goto err;
	v->inputGlyphCount = b[0]<<8 | b[1];
	if((b = otfreadn(o, (v->inputGlyphCount-1)*2)) == nil)
		goto err;
	v->inputSequence = malloc((v->inputGlyphCount-1)*sizeof(*v->inputSequence));
	for(int i = 0; i < (v->inputGlyphCount-1); i++)
		v->inputSequence[i] = b[0+i*2]<<8 | b[1+i*2];
	if((b = otfreadn(o, 2)) == nil)
		goto err;
	v->lookaheadGlyphCount = b[0]<<8 | b[1];
	if((b = otfreadn(o, v->lookaheadGlyphCount*2)) == nil)
		goto err;
	v->lookaheadSequence = malloc(v->lookaheadGlyphCount*sizeof(*v->lookaheadSequence));
	for(int i = 0; i < v->lookaheadGlyphCount; i++)
		v->lookaheadSequence[i] = b[0+i*2]<<8 | b[1+i*2];
	if((b = otfreadn(o, 2)) == nil)
		goto err;
	v->seqLookupCount = b[0]<<8 | b[1];
	if(otfarray(o, &v->seqLookupRecords, read_SequenceLookup, sizeof(SequenceLookup), v->seqLookupCount) < 0){
		werrstr("%s: %r", "seqLookupRecords");
		goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "ChainedSequenceRule");
	return -1;
}

void
print_ChainedSequenceRule(Otfile *f, int indent, Otf *o, ChainedSequenceRule *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "backtrackGlyphCount", v->backtrackGlyphCount);
	for(int i = 0; i < v->backtrackGlyphCount; i++)
		f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "backtrackSequence", i, v->backtrackSequence[i]);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "inputGlyphCount", v->inputGlyphCount);
	for(int i = 0; i < (v->inputGlyphCount-1); i++)
		f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "inputSequence", i, v->inputSequence[i]);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "lookaheadGlyphCount", v->lookaheadGlyphCount);
	for(int i = 0; i < v->lookaheadGlyphCount; i++)
		f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "lookaheadSequence", i, v->lookaheadSequence[i]);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "seqLookupCount", v->seqLookupCount);
	for(int i = 0; i < v->seqLookupCount; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "seqLookupRecords", i);
		print_SequenceLookup(f, indent+indentΔ, o, &v->seqLookupRecords[i]);
	}
	USED(o);
}

int
read_ChainedSequenceRuleSet(Otf *o, ChainedSequenceRuleSet *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 2)) == nil)
		goto err;
	v->chainedSeqRuleCount = b[0]<<8 | b[1];
	if((b = otfreadn(o, v->chainedSeqRuleCount*2)) == nil)
		goto err;
	v->chainedSeqRuleOffsets = malloc(v->chainedSeqRuleCount*sizeof(*v->chainedSeqRuleOffsets));
	for(int i = 0; i < v->chainedSeqRuleCount; i++)
		v->chainedSeqRuleOffsets[i] = b[0+i*2]<<8 | b[1+i*2];
	if(v->chainedSeqRuleCount > 0){
		v->chainedSeqRule = calloc(v->chainedSeqRuleCount, sizeof(*v->chainedSeqRule));
		for(int i = 0; i < v->chainedSeqRuleCount; i++){
			if(v->chainedSeqRuleOffsets[i] == 0)
				continue;
			if(otfpushrange(o, v->chainedSeqRuleOffsets[i], -1))
				goto err;
			int r = read_ChainedSequenceRule(o, v->chainedSeqRule+i);
			if(otfpoprange(o) < 0)
				goto err;
			if(r < 0){
				memset(v->chainedSeqRule+i, 0, sizeof(*v->chainedSeqRule));
				break;
			}
		}
	}
	return 0;
err:
	werrstr("%s: %r", "ChainedSequenceRuleSet");
	return -1;
}

void
print_ChainedSequenceRuleSet(Otfile *f, int indent, Otf *o, ChainedSequenceRuleSet *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "chainedSeqRuleCount", v->chainedSeqRuleCount);
	for(int i = 0; i < v->chainedSeqRuleCount; i++)
		f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "chainedSeqRuleOffsets", i, v->chainedSeqRuleOffsets[i]);
	for(int i = 0; i < v->chainedSeqRuleCount; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "chainedSeqRule", i);
		print_ChainedSequenceRule(f, indent+indentΔ, o, &v->chainedSeqRule[i]);
	}
	USED(o);
}

int
read_ChainedSequenceContextFormat1(Otf *o, ChainedSequenceContextFormat1 *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 4)) == nil)
		goto err;
	v->coverageOffset = b[0]<<8 | b[1];
	v->chainedSeqRuleSetCount = b[2]<<8 | b[3];
	if((b = otfreadn(o, v->chainedSeqRuleSetCount*2)) == nil)
		goto err;
	v->chainedSeqRuleSetOffsets = malloc(v->chainedSeqRuleSetCount*sizeof(*v->chainedSeqRuleSetOffsets));
	for(int i = 0; i < v->chainedSeqRuleSetCount; i++)
		v->chainedSeqRuleSetOffsets[i] = b[0+i*2]<<8 | b[1+i*2];
	if(v->chainedSeqRuleSetCount > 0){
		v->chainedSeqRuleSet = calloc(v->chainedSeqRuleSetCount, sizeof(*v->chainedSeqRuleSet));
		for(int i = 0; i < v->chainedSeqRuleSetCount; i++){
			if(v->chainedSeqRuleSetOffsets[i] == 0)
				continue;
			if(otfpushrange(o, v->chainedSeqRuleSetOffsets[i], -1))
				goto err;
			int r = read_ChainedSequenceRuleSet(o, v->chainedSeqRuleSet+i);
			if(otfpoprange(o) < 0)
				goto err;
			if(r < 0){
				memset(v->chainedSeqRuleSet+i, 0, sizeof(*v->chainedSeqRuleSet));
				break;
			}
		}
	}
	return 0;
err:
	werrstr("%s: %r", "ChainedSequenceContextFormat1");
	return -1;
}

void
print_ChainedSequenceContextFormat1(Otfile *f, int indent, Otf *o, ChainedSequenceContextFormat1 *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "coverageOffset", v->coverageOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "chainedSeqRuleSetCount", v->chainedSeqRuleSetCount);
	for(int i = 0; i < v->chainedSeqRuleSetCount; i++)
		f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "chainedSeqRuleSetOffsets", i, v->chainedSeqRuleSetOffsets[i]);
	for(int i = 0; i < v->chainedSeqRuleSetCount; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "chainedSeqRuleSet", i);
		print_ChainedSequenceRuleSet(f, indent+indentΔ, o, &v->chainedSeqRuleSet[i]);
	}
	USED(o);
}

int
read_ChainedClassSequenceRuleSet(Otf *o, ChainedClassSequenceRuleSet *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 2)) == nil)
		goto err;
	v->chainedClassSeqRuleCount = b[0]<<8 | b[1];
	if((b = otfreadn(o, v->chainedClassSeqRuleCount*2)) == nil)
		goto err;
	v->chainedClassSeqRuleOffsets = malloc(v->chainedClassSeqRuleCount*sizeof(*v->chainedClassSeqRuleOffsets));
	for(int i = 0; i < v->chainedClassSeqRuleCount; i++)
		v->chainedClassSeqRuleOffsets[i] = b[0+i*2]<<8 | b[1+i*2];
	if(v->chainedClassSeqRuleCount > 0){
		v->chainedClassSeqRules = calloc(v->chainedClassSeqRuleCount, sizeof(*v->chainedClassSeqRules));
		for(int i = 0; i < v->chainedClassSeqRuleCount; i++){
			if(v->chainedClassSeqRuleOffsets[i] == 0)
				continue;
			if(otfpushrange(o, v->chainedClassSeqRuleOffsets[i], -1))
				goto err;
			int r = read_ChainedSequenceRule(o, v->chainedClassSeqRules+i);
			if(otfpoprange(o) < 0)
				goto err;
			if(r < 0){
				memset(v->chainedClassSeqRules+i, 0, sizeof(*v->chainedClassSeqRules));
				break;
			}
		}
	}
	return 0;
err:
	werrstr("%s: %r", "ChainedClassSequenceRuleSet");
	return -1;
}

void
print_ChainedClassSequenceRuleSet(Otfile *f, int indent, Otf *o, ChainedClassSequenceRuleSet *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "chainedClassSeqRuleCount", v->chainedClassSeqRuleCount);
	for(int i = 0; i < v->chainedClassSeqRuleCount; i++)
		f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "chainedClassSeqRuleOffsets", i, v->chainedClassSeqRuleOffsets[i]);
	for(int i = 0; i < v->chainedClassSeqRuleCount; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "chainedClassSeqRules", i);
		print_ChainedSequenceRule(f, indent+indentΔ, o, &v->chainedClassSeqRules[i]);
	}
	USED(o);
}

int
read_ChainedSequenceContextFormat2(Otf *o, ChainedSequenceContextFormat2 *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 10)) == nil)
		goto err;
	v->coverageOffset = b[0]<<8 | b[1];
	v->backtrackClassDefOffset = b[2]<<8 | b[3];
	v->inputClassDefOffset = b[4]<<8 | b[5];
	v->lookaheadClassDefOffset = b[6]<<8 | b[7];
	v->chainedClassSeqRuleSetCount = b[8]<<8 | b[9];
	if((b = otfreadn(o, v->chainedClassSeqRuleSetCount*2)) == nil)
		goto err;
	v->chainedClassSeqRuleSetOffsets = malloc(v->chainedClassSeqRuleSetCount*sizeof(*v->chainedClassSeqRuleSetOffsets));
	for(int i = 0; i < v->chainedClassSeqRuleSetCount; i++)
		v->chainedClassSeqRuleSetOffsets[i] = b[0+i*2]<<8 | b[1+i*2];
	if(v->chainedClassSeqRuleSetCount > 0){
		v->chainedClassSequenceRuleSets = calloc(v->chainedClassSeqRuleSetCount, sizeof(*v->chainedClassSequenceRuleSets));
		for(int i = 0; i < v->chainedClassSeqRuleSetCount; i++){
			if(v->chainedClassSeqRuleSetOffsets[i] == 0)
				continue;
			if(otfpushrange(o, v->chainedClassSeqRuleSetOffsets[i], -1))
				goto err;
			int r = read_ChainedClassSequenceRuleSet(o, v->chainedClassSequenceRuleSets+i);
			if(otfpoprange(o) < 0)
				goto err;
			if(r < 0){
				memset(v->chainedClassSequenceRuleSets+i, 0, sizeof(*v->chainedClassSequenceRuleSets));
				break;
			}
		}
	}
	return 0;
err:
	werrstr("%s: %r", "ChainedSequenceContextFormat2");
	return -1;
}

void
print_ChainedSequenceContextFormat2(Otfile *f, int indent, Otf *o, ChainedSequenceContextFormat2 *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "coverageOffset", v->coverageOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "backtrackClassDefOffset", v->backtrackClassDefOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "inputClassDefOffset", v->inputClassDefOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "lookaheadClassDefOffset", v->lookaheadClassDefOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "chainedClassSeqRuleSetCount", v->chainedClassSeqRuleSetCount);
	for(int i = 0; i < v->chainedClassSeqRuleSetCount; i++)
		f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "chainedClassSeqRuleSetOffsets", i, v->chainedClassSeqRuleSetOffsets[i]);
	for(int i = 0; i < v->chainedClassSeqRuleSetCount; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "chainedClassSequenceRuleSets", i);
		print_ChainedClassSequenceRuleSet(f, indent+indentΔ, o, &v->chainedClassSequenceRuleSets[i]);
	}
	USED(o);
}

int
read_ChainedSequenceContextFormat3(Otf *o, ChainedSequenceContextFormat3 *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 2)) == nil)
		goto err;
	v->backtrackGlyphCount = b[0]<<8 | b[1];
	if((b = otfreadn(o, v->backtrackGlyphCount*2)) == nil)
		goto err;
	v->backtrackCoverageOffsets = malloc(v->backtrackGlyphCount*sizeof(*v->backtrackCoverageOffsets));
	for(int i = 0; i < v->backtrackGlyphCount; i++)
		v->backtrackCoverageOffsets[i] = b[0+i*2]<<8 | b[1+i*2];
	if((b = otfreadn(o, 2)) == nil)
		goto err;
	v->inputGlyphCount = b[0]<<8 | b[1];
	if((b = otfreadn(o, v->inputGlyphCount*2)) == nil)
		goto err;
	v->inputCoverageOffsets = malloc(v->inputGlyphCount*sizeof(*v->inputCoverageOffsets));
	for(int i = 0; i < v->inputGlyphCount; i++)
		v->inputCoverageOffsets[i] = b[0+i*2]<<8 | b[1+i*2];
	if((b = otfreadn(o, 2)) == nil)
		goto err;
	v->lookaheadGlyphCount = b[0]<<8 | b[1];
	if((b = otfreadn(o, v->lookaheadGlyphCount*2)) == nil)
		goto err;
	v->lookaheadCoverageOffsets = malloc(v->lookaheadGlyphCount*sizeof(*v->lookaheadCoverageOffsets));
	for(int i = 0; i < v->lookaheadGlyphCount; i++)
		v->lookaheadCoverageOffsets[i] = b[0+i*2]<<8 | b[1+i*2];
	if((b = otfreadn(o, 2)) == nil)
		goto err;
	v->seqLookupCount = b[0]<<8 | b[1];
	if(otfarray(o, &v->seqLookupRecords, read_SequenceLookup, sizeof(SequenceLookup), v->seqLookupCount) < 0){
		werrstr("%s: %r", "seqLookupRecords");
		goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "ChainedSequenceContextFormat3");
	return -1;
}

void
print_ChainedSequenceContextFormat3(Otfile *f, int indent, Otf *o, ChainedSequenceContextFormat3 *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "backtrackGlyphCount", v->backtrackGlyphCount);
	for(int i = 0; i < v->backtrackGlyphCount; i++)
		f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "backtrackCoverageOffsets", i, v->backtrackCoverageOffsets[i]);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "inputGlyphCount", v->inputGlyphCount);
	for(int i = 0; i < v->inputGlyphCount; i++)
		f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "inputCoverageOffsets", i, v->inputCoverageOffsets[i]);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "lookaheadGlyphCount", v->lookaheadGlyphCount);
	for(int i = 0; i < v->lookaheadGlyphCount; i++)
		f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "lookaheadCoverageOffsets", i, v->lookaheadCoverageOffsets[i]);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "seqLookupCount", v->seqLookupCount);
	for(int i = 0; i < v->seqLookupCount; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "seqLookupRecords", i);
		print_SequenceLookup(f, indent+indentΔ, o, &v->seqLookupRecords[i]);
	}
	USED(o);
}

int
read_ChainedSequenceContext(Otf *o, ChainedSequenceContext *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 2)) == nil)
		goto err;
	v->format = b[0]<<8 | b[1];
	if(v->format == 1){
		if(read_ChainedSequenceContextFormat1(o, &v->f1) < 0){
			werrstr("%s: %r", "f1");
			goto err;
		}
	}
	if(v->format == 2){
		if(read_ChainedSequenceContextFormat2(o, &v->f2) < 0){
			werrstr("%s: %r", "f2");
			goto err;
		}
	}
	if(v->format == 3){
		if(read_ChainedSequenceContextFormat3(o, &v->f3) < 0){
			werrstr("%s: %r", "f3");
			goto err;
		}
	}
	return 0;
err:
	werrstr("%s: %r", "ChainedSequenceContext");
	return -1;
}

void
print_ChainedSequenceContext(Otfile *f, int indent, Otf *o, ChainedSequenceContext *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "format", v->format);
	if(v->format == 1){
		f->print(f->aux, "%*s%s:\n", indent, "", "f1");
		print_ChainedSequenceContextFormat1(f, indent+indentΔ, o, &v->f1);
	}
	if(v->format == 2){
		f->print(f->aux, "%*s%s:\n", indent, "", "f2");
		print_ChainedSequenceContextFormat2(f, indent+indentΔ, o, &v->f2);
	}
	if(v->format == 3){
		f->print(f->aux, "%*s%s:\n", indent, "", "f3");
		print_ChainedSequenceContextFormat3(f, indent+indentΔ, o, &v->f3);
	}
	USED(o);
}

int
read_PairValue(Otf *o, PairValue *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 2)) == nil)
		goto err;
	v->secondGlyph = b[0]<<8 | b[1];
	if(read_ValueRecord(o, &v->valueRecord1) < 0){
		werrstr("%s: %r", "valueRecord1");
		goto err;
	}
	if(read_ValueRecord(o, &v->valueRecord2) < 0){
		werrstr("%s: %r", "valueRecord2");
		goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "PairValue");
	return -1;
}

void
print_PairValue(Otfile *f, int indent, Otf *o, PairValue *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "secondGlyph", v->secondGlyph);
	f->print(f->aux, "%*s%s:\n", indent, "", "valueRecord1");
	print_ValueRecord(f, indent+indentΔ, o, &v->valueRecord1);
	f->print(f->aux, "%*s%s:\n", indent, "", "valueRecord2");
	print_ValueRecord(f, indent+indentΔ, o, &v->valueRecord2);
	USED(o);
}

int
read_PairSet(Otf *o, PairSet *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 2)) == nil)
		goto err;
	v->pairValueCount = b[0]<<8 | b[1];
	if(otfarray(o, &v->pairValueRecords, read_PairValue, sizeof(PairValue), v->pairValueCount) < 0){
		werrstr("%s: %r", "pairValueRecords");
		goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "PairSet");
	return -1;
}

void
print_PairSet(Otfile *f, int indent, Otf *o, PairSet *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "pairValueCount", v->pairValueCount);
	for(int i = 0; i < v->pairValueCount; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "pairValueRecords", i);
		print_PairValue(f, indent+indentΔ, o, &v->pairValueRecords[i]);
	}
	USED(o);
}

int
read_PairPosFormat1(Otf *o, PairPosFormat1 *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 2)) == nil)
		goto err;
	v->pairSetCount = b[0]<<8 | b[1];
	if((b = otfreadn(o, v->pairSetCount*2)) == nil)
		goto err;
	v->pairSetOffsets = malloc(v->pairSetCount*sizeof(*v->pairSetOffsets));
	for(int i = 0; i < v->pairSetCount; i++)
		v->pairSetOffsets[i] = b[0+i*2]<<8 | b[1+i*2];
	if(v->pairSetCount > 0){
		v->pairSets = calloc(v->pairSetCount, sizeof(*v->pairSets));
		for(int i = 0; i < v->pairSetCount; i++){
			if(v->pairSetOffsets[i] == 0)
				continue;
			if(otfpushrange(o, v->pairSetOffsets[i], -1))
				goto err;
			int r = read_PairSet(o, v->pairSets+i);
			if(otfpoprange(o) < 0)
				goto err;
			if(r < 0){
				memset(v->pairSets+i, 0, sizeof(*v->pairSets));
				break;
			}
		}
	}
	return 0;
err:
	werrstr("%s: %r", "PairPosFormat1");
	return -1;
}

void
print_PairPosFormat1(Otfile *f, int indent, Otf *o, PairPosFormat1 *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "pairSetCount", v->pairSetCount);
	for(int i = 0; i < v->pairSetCount; i++)
		f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "pairSetOffsets", i, v->pairSetOffsets[i]);
	for(int i = 0; i < v->pairSetCount; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "pairSets", i);
		print_PairSet(f, indent+indentΔ, o, &v->pairSets[i]);
	}
	USED(o);
}

int
read_Class2(Otf *o, Class2 *v)
{
	u8int *b = nil; USED(b);
	if(read_ValueRecord(o, &v->valueRecord1) < 0){
		werrstr("%s: %r", "valueRecord1");
		goto err;
	}
	if(read_ValueRecord(o, &v->valueRecord2) < 0){
		werrstr("%s: %r", "valueRecord2");
		goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "Class2");
	return -1;
}

void
print_Class2(Otfile *f, int indent, Otf *o, Class2 *v)
{
	f->print(f->aux, "%*s%s:\n", indent, "", "valueRecord1");
	print_ValueRecord(f, indent+indentΔ, o, &v->valueRecord1);
	f->print(f->aux, "%*s%s:\n", indent, "", "valueRecord2");
	print_ValueRecord(f, indent+indentΔ, o, &v->valueRecord2);
	USED(o);
}

int
read_Class1(Otf *o, Class1 *v)
{
	u8int *b = nil; USED(b);
	if(otfarray(o, &v->class2Records, read_Class2, sizeof(Class2), o->class2Count) < 0){
		werrstr("%s: %r", "class2Records");
		goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "Class1");
	return -1;
}

void
print_Class1(Otfile *f, int indent, Otf *o, Class1 *v)
{
	for(int i = 0; i < o->class2Count; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "class2Records", i);
		print_Class2(f, indent+indentΔ, o, &v->class2Records[i]);
	}
	USED(o);
}

int
read_PairPosFormat2(Otf *o, PairPosFormat2 *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 8)) == nil)
		goto err;
	v->classDef1Offset = b[0]<<8 | b[1];
	v->classDef2Offset = b[2]<<8 | b[3];
	v->class1Count = b[4]<<8 | b[5];
	v->class2Count = b[6]<<8 | b[7];
	o->class2Count = v->class2Count;
	if(otfarray(o, &v->class1Records, read_Class1, sizeof(Class1), v->class1Count) < 0){
		werrstr("%s: %r", "class1Records");
		goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "PairPosFormat2");
	return -1;
}

void
print_PairPosFormat2(Otfile *f, int indent, Otf *o, PairPosFormat2 *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "classDef1Offset", v->classDef1Offset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "classDef2Offset", v->classDef2Offset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "class1Count", v->class1Count);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "class2Count", v->class2Count);
	o->class2Count = v->class2Count;
	for(int i = 0; i < v->class1Count; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "class1Records", i);
		print_Class1(f, indent+indentΔ, o, &v->class1Records[i]);
	}
	USED(o);
}

int
read_PairPos(Otf *o, PairPos *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 8)) == nil)
		goto err;
	v->format = b[0]<<8 | b[1];
	if(v->format != 1 && v->format != 2){
		werrstr("%s: invalid value: %d (%#ux)", "format", v->format, v->format);
		goto err;
	}
	v->coverageOffset = b[2]<<8 | b[3];
	v->valueFormat1 = b[4]<<8 | b[5];
	v->valueFormat2 = b[6]<<8 | b[7];
	if(v->format == 1){
		if(read_PairPosFormat1(o, &v->f1) < 0){
			werrstr("%s: %r", "f1");
			goto err;
		}
	}
	if(v->format == 2){
		if(read_PairPosFormat2(o, &v->f2) < 0){
			werrstr("%s: %r", "f2");
			goto err;
		}
	}
	return 0;
err:
	werrstr("%s: %r", "PairPos");
	return -1;
}

void
print_PairPos(Otfile *f, int indent, Otf *o, PairPos *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "format", v->format);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "coverageOffset", v->coverageOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "valueFormat1", v->valueFormat1);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "valueFormat2", v->valueFormat2);
	if(v->format == 1){
		f->print(f->aux, "%*s%s:\n", indent, "", "f1");
		print_PairPosFormat1(f, indent+indentΔ, o, &v->f1);
	}
	if(v->format == 2){
		f->print(f->aux, "%*s%s:\n", indent, "", "f2");
		print_PairPosFormat2(f, indent+indentΔ, o, &v->f2);
	}
	USED(o);
}

int
read_EntryExit(Otf *o, EntryExit *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 4)) == nil)
		goto err;
	v->entryAnchorOffset = b[0]<<8 | b[1];
	v->exitAnchorOffset = b[2]<<8 | b[3];
	return 0;
err:
	werrstr("%s: %r", "EntryExit");
	return -1;
}

void
print_EntryExit(Otfile *f, int indent, Otf *o, EntryExit *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "entryAnchorOffset", v->entryAnchorOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "exitAnchorOffset", v->exitAnchorOffset);
	USED(o);
}

int
read_CursivePos(Otf *o, CursivePos *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 6)) == nil)
		goto err;
	u16int format = b[0]<<8 | b[1];
	if(format != 1){
		werrstr("%s: invalid value: %d (%#ux)", "format", format, format);
		goto err;
	}
	v->coverageOffset = b[2]<<8 | b[3];
	v->entryExitCount = b[4]<<8 | b[5];
	if(otfarray(o, &v->entryExitRecords, read_EntryExit, sizeof(EntryExit), v->entryExitCount) < 0){
		werrstr("%s: %r", "entryExitRecords");
		goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "CursivePos");
	return -1;
}

void
print_CursivePos(Otfile *f, int indent, Otf *o, CursivePos *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "coverageOffset", v->coverageOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "entryExitCount", v->entryExitCount);
	for(int i = 0; i < v->entryExitCount; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "entryExitRecords", i);
		print_EntryExit(f, indent+indentΔ, o, &v->entryExitRecords[i]);
	}
	USED(o);
}

int
read_Lookup(Otf *o, Lookup *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 6)) == nil)
		goto err;
	v->lookupType = b[0]<<8 | b[1];
	v->lookupFlag = b[2]<<8 | b[3];
	v->subTableCount = b[4]<<8 | b[5];
	if((b = otfreadn(o, v->subTableCount*2)) == nil)
		goto err;
	v->subtableOffsets = malloc(v->subTableCount*sizeof(*v->subtableOffsets));
	for(int i = 0; i < v->subTableCount; i++)
		v->subtableOffsets[i] = b[0+i*2]<<8 | b[1+i*2];
	if((b = otfreadn(o, 2)) == nil)
		goto err;
	v->markFilteringSet = b[0]<<8 | b[1];
	if((v->lookupType==LOOKUP_TYPE_SINGLE_ADJUSTMENT)){
		if(v->subTableCount > 0){
			v->singleAdjustment = calloc(v->subTableCount, sizeof(*v->singleAdjustment));
			for(int i = 0; i < v->subTableCount; i++){
				if(v->subtableOffsets[i] == 0)
					continue;
				if(otfpushrange(o, v->subtableOffsets[i], -1))
					goto err;
				int r = read_SinglePos(o, v->singleAdjustment+i);
				if(otfpoprange(o) < 0)
					goto err;
				if(r < 0){
					memset(v->singleAdjustment+i, 0, sizeof(*v->singleAdjustment));
					break;
				}
			}
		}
	}
	if((v->lookupType==LOOKUP_TYPE_PAIR_ADJUSTMENT)){
		if(v->subTableCount > 0){
			v->pairAdjustment = calloc(v->subTableCount, sizeof(*v->pairAdjustment));
			for(int i = 0; i < v->subTableCount; i++){
				if(v->subtableOffsets[i] == 0)
					continue;
				if(otfpushrange(o, v->subtableOffsets[i], -1))
					goto err;
				int r = read_PairPos(o, v->pairAdjustment+i);
				if(otfpoprange(o) < 0)
					goto err;
				if(r < 0){
					memset(v->pairAdjustment+i, 0, sizeof(*v->pairAdjustment));
					break;
				}
			}
		}
	}
	if((v->lookupType==LOOKUP_TYPE_CURSIVE_ATTACHMENT)){
		if(v->subTableCount > 0){
			v->cursiveAttachment = calloc(v->subTableCount, sizeof(*v->cursiveAttachment));
			for(int i = 0; i < v->subTableCount; i++){
				if(v->subtableOffsets[i] == 0)
					continue;
				if(otfpushrange(o, v->subtableOffsets[i], -1))
					goto err;
				int r = read_CursivePos(o, v->cursiveAttachment+i);
				if(otfpoprange(o) < 0)
					goto err;
				if(r < 0){
					memset(v->cursiveAttachment+i, 0, sizeof(*v->cursiveAttachment));
					break;
				}
			}
		}
	}
	if((v->lookupType==LOOKUP_TYPE_CHAINED_CONTEXTS_POSITIONING)){
		if(v->subTableCount > 0){
			v->chainedContextsPositioning = calloc(v->subTableCount, sizeof(*v->chainedContextsPositioning));
			for(int i = 0; i < v->subTableCount; i++){
				if(v->subtableOffsets[i] == 0)
					continue;
				if(otfpushrange(o, v->subtableOffsets[i], -1))
					goto err;
				int r = read_ChainedSequenceContext(o, v->chainedContextsPositioning+i);
				if(otfpoprange(o) < 0)
					goto err;
				if(r < 0){
					memset(v->chainedContextsPositioning+i, 0, sizeof(*v->chainedContextsPositioning));
					break;
				}
			}
		}
	}
	return 0;
err:
	werrstr("%s: %r", "Lookup");
	return -1;
}

void
print_Lookup(Otfile *f, int indent, Otf *o, Lookup *v)
{
	f->print(f->aux, "%*s%s: %ud%s\n", indent, "", "lookupType", v->lookupType, v->lookupType==LOOKUP_TYPE_SINGLE_ADJUSTMENT?" LOOKUP_TYPE_SINGLE_ADJUSTMENT": v->lookupType==LOOKUP_TYPE_PAIR_ADJUSTMENT?" LOOKUP_TYPE_PAIR_ADJUSTMENT": v->lookupType==LOOKUP_TYPE_CURSIVE_ATTACHMENT?" LOOKUP_TYPE_CURSIVE_ATTACHMENT": v->lookupType==LOOKUP_TYPE_MARK_TO_BASE_ATTACHMENT?" LOOKUP_TYPE_MARK_TO_BASE_ATTACHMENT": v->lookupType==LOOKUP_TYPE_MARK_TO_LIGATURE_ATTACHMENT?" LOOKUP_TYPE_MARK_TO_LIGATURE_ATTACHMENT": v->lookupType==LOOKUP_TYPE_MARK_TO_MARK_ATTACHMENT?" LOOKUP_TYPE_MARK_TO_MARK_ATTACHMENT": v->lookupType==LOOKUP_TYPE_CONTEXTUAL_POSITIONING?" LOOKUP_TYPE_CONTEXTUAL_POSITIONING": v->lookupType==LOOKUP_TYPE_CHAINED_CONTEXTS_POSITIONING?" LOOKUP_TYPE_CHAINED_CONTEXTS_POSITIONING": v->lookupType==LOOKUP_TYPE_POSITIONING_EXTENSION?" LOOKUP_TYPE_POSITIONING_EXTENSION":"");
	f->print(f->aux, "%*s%s: %#ux%s%s%s%s%s\n", indent, "", "lookupFlag", v->lookupFlag, (v->lookupFlag&LOOKUP_FL_RIGHT_TO_LEFT)?" LOOKUP_FL_RIGHT_TO_LEFT":"", (v->lookupFlag&LOOKUP_FL_IGNORE_BASE_GLYPHS)?" LOOKUP_FL_IGNORE_BASE_GLYPHS":"", (v->lookupFlag&LOOKUP_FL_IGNORE_LIGATURES)?" LOOKUP_FL_IGNORE_LIGATURES":"", (v->lookupFlag&LOOKUP_FL_IGNORE_MARKS)?" LOOKUP_FL_IGNORE_MARKS":"", (v->lookupFlag&LOOKUP_FL_USE_MARK_FILTERING_SET)?" LOOKUP_FL_USE_MARK_FILTERING_SET":"");
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "subTableCount", v->subTableCount);
	for(int i = 0; i < v->subTableCount; i++)
		f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "subtableOffsets", i, v->subtableOffsets[i]);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "markFilteringSet", v->markFilteringSet);
	if((v->lookupType==LOOKUP_TYPE_SINGLE_ADJUSTMENT)){
		for(int i = 0; i < v->subTableCount; i++){
			f->print(f->aux, "%*s%s[%d]:\n", indent, "", "singleAdjustment", i);
			print_SinglePos(f, indent+indentΔ, o, &v->singleAdjustment[i]);
		}
	}
	if((v->lookupType==LOOKUP_TYPE_PAIR_ADJUSTMENT)){
		for(int i = 0; i < v->subTableCount; i++){
			f->print(f->aux, "%*s%s[%d]:\n", indent, "", "pairAdjustment", i);
			print_PairPos(f, indent+indentΔ, o, &v->pairAdjustment[i]);
		}
	}
	if((v->lookupType==LOOKUP_TYPE_CURSIVE_ATTACHMENT)){
		for(int i = 0; i < v->subTableCount; i++){
			f->print(f->aux, "%*s%s[%d]:\n", indent, "", "cursiveAttachment", i);
			print_CursivePos(f, indent+indentΔ, o, &v->cursiveAttachment[i]);
		}
	}
	if((v->lookupType==LOOKUP_TYPE_CHAINED_CONTEXTS_POSITIONING)){
		for(int i = 0; i < v->subTableCount; i++){
			f->print(f->aux, "%*s%s[%d]:\n", indent, "", "chainedContextsPositioning", i);
			print_ChainedSequenceContext(f, indent+indentΔ, o, &v->chainedContextsPositioning[i]);
		}
	}
	USED(o);
}

int
read_LookupList(Otf *o, LookupList *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 2)) == nil)
		goto err;
	v->lookupCount = b[0]<<8 | b[1];
	if((b = otfreadn(o, v->lookupCount*2)) == nil)
		goto err;
	v->lookupOffsets = malloc(v->lookupCount*sizeof(*v->lookupOffsets));
	for(int i = 0; i < v->lookupCount; i++)
		v->lookupOffsets[i] = b[0+i*2]<<8 | b[1+i*2];
	if(v->lookupCount > 0){
		v->lookups = calloc(v->lookupCount, sizeof(*v->lookups));
		for(int i = 0; i < v->lookupCount; i++){
			if(v->lookupOffsets[i] == 0)
				continue;
			if(otfpushrange(o, v->lookupOffsets[i], -1))
				goto err;
			int r = read_Lookup(o, v->lookups+i);
			if(otfpoprange(o) < 0)
				goto err;
			if(r < 0){
				memset(v->lookups+i, 0, sizeof(*v->lookups));
				break;
			}
		}
	}
	return 0;
err:
	werrstr("%s: %r", "LookupList");
	return -1;
}

void
print_LookupList(Otfile *f, int indent, Otf *o, LookupList *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "lookupCount", v->lookupCount);
	for(int i = 0; i < v->lookupCount; i++)
		f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "lookupOffsets", i, v->lookupOffsets[i]);
	for(int i = 0; i < v->lookupCount; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "lookups", i);
		print_Lookup(f, indent+indentΔ, o, &v->lookups[i]);
	}
	USED(o);
}

int
read_TableGPOS(Otf *o, TableGPOS *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 10)) == nil)
		goto err;
	u16int majorVersion = b[0]<<8 | b[1];
	if(majorVersion != 1){
		werrstr("%s: invalid value: %d (%#ux)", "majorVersion", majorVersion, majorVersion);
		goto err;
	}
	v->minorVersion = b[2]<<8 | b[3];
	if(v->minorVersion > 1){
		werrstr("%s: invalid value: %d (%#ux)", "minorVersion", v->minorVersion, v->minorVersion);
		goto err;
	}
	v->scriptListOffset = b[4]<<8 | b[5];
	v->featureListOffset = b[6]<<8 | b[7];
	v->lookupListOffset = b[8]<<8 | b[9];
	if(v->minorVersion == 1){
		if((b = otfreadn(o, 4)) == nil)
			goto err;
		v->featureVariationsOffset = b[0]<<24 | b[1]<<16 | b[2]<<8 | b[3];
	}
	if(v->scriptListOffset != 0){
		if(otfpushrange(o, v->scriptListOffset, -1) < 0)
			goto err;
		v->scriptList = calloc(1, sizeof(*v->scriptList));
		if(read_ScriptList(o, v->scriptList) < 0){
			werrstr("%s: %r", "scriptList");
			goto err;
		}
		if(otfpoprange(o) < 0)
			goto err;
	}
	if(v->featureListOffset != 0){
		if(otfpushrange(o, v->featureListOffset, -1) < 0)
			goto err;
		v->featureList = calloc(1, sizeof(*v->featureList));
		if(read_FeatureList(o, v->featureList) < 0){
			werrstr("%s: %r", "featureList");
			goto err;
		}
		if(otfpoprange(o) < 0)
			goto err;
	}
	if(v->lookupListOffset != 0){
		if(otfpushrange(o, v->lookupListOffset, -1) < 0)
			goto err;
		v->lookupList = calloc(1, sizeof(*v->lookupList));
		if(read_LookupList(o, v->lookupList) < 0){
			werrstr("%s: %r", "lookupList");
			goto err;
		}
		if(otfpoprange(o) < 0)
			goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "TableGPOS");
	return -1;
}

void
print_TableGPOS(Otfile *f, int indent, Otf *o, TableGPOS *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "minorVersion", v->minorVersion);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "scriptListOffset", v->scriptListOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "featureListOffset", v->featureListOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "lookupListOffset", v->lookupListOffset);
	if(v->minorVersion == 1)
		f->print(f->aux, "%*s%s: %ud\n", indent, "", "featureVariationsOffset", v->featureVariationsOffset);
	f->print(f->aux, "%*s%s:\n", indent, "", "scriptList");
	if(v->scriptList != nil)
		print_ScriptList(f, indent+indentΔ, o, v->scriptList);
	f->print(f->aux, "%*s%s:\n", indent, "", "featureList");
	if(v->featureList != nil)
		print_FeatureList(f, indent+indentΔ, o, v->featureList);
	f->print(f->aux, "%*s%s:\n", indent, "", "lookupList");
	if(v->lookupList != nil)
		print_LookupList(f, indent+indentΔ, o, v->lookupList);
	USED(o);
}

int
read_TableGSUB(Otf *o, TableGSUB *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 10)) == nil)
		goto err;
	u16int majorVersion = b[0]<<8 | b[1];
	if(majorVersion != 1){
		werrstr("%s: invalid value: %d (%#ux)", "majorVersion", majorVersion, majorVersion);
		goto err;
	}
	v->minorVersion = b[2]<<8 | b[3];
	if(v->minorVersion > 1){
		werrstr("%s: invalid value: %d (%#ux)", "minorVersion", v->minorVersion, v->minorVersion);
		goto err;
	}
	v->scriptListOffset = b[4]<<8 | b[5];
	v->featureListOffset = b[6]<<8 | b[7];
	v->lookupListOffset = b[8]<<8 | b[9];
	if(v->minorVersion == 1){
		if((b = otfreadn(o, 4)) == nil)
			goto err;
		v->featureVariationsOffset = b[0]<<24 | b[1]<<16 | b[2]<<8 | b[3];
	}
	if(v->scriptListOffset != 0){
		if(otfpushrange(o, v->scriptListOffset, -1) < 0)
			goto err;
		v->scriptList = calloc(1, sizeof(*v->scriptList));
		if(read_ScriptList(o, v->scriptList) < 0){
			werrstr("%s: %r", "scriptList");
			goto err;
		}
		if(otfpoprange(o) < 0)
			goto err;
	}
	if(v->featureListOffset != 0){
		if(otfpushrange(o, v->featureListOffset, -1) < 0)
			goto err;
		v->featureList = calloc(1, sizeof(*v->featureList));
		if(read_FeatureList(o, v->featureList) < 0){
			werrstr("%s: %r", "featureList");
			goto err;
		}
		if(otfpoprange(o) < 0)
			goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "TableGSUB");
	return -1;
}

void
print_TableGSUB(Otfile *f, int indent, Otf *o, TableGSUB *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "minorVersion", v->minorVersion);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "scriptListOffset", v->scriptListOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "featureListOffset", v->featureListOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "lookupListOffset", v->lookupListOffset);
	if(v->minorVersion == 1)
		f->print(f->aux, "%*s%s: %ud\n", indent, "", "featureVariationsOffset", v->featureVariationsOffset);
	f->print(f->aux, "%*s%s:\n", indent, "", "scriptList");
	if(v->scriptList != nil)
		print_ScriptList(f, indent+indentΔ, o, v->scriptList);
	f->print(f->aux, "%*s%s:\n", indent, "", "featureList");
	if(v->featureList != nil)
		print_FeatureList(f, indent+indentΔ, o, v->featureList);
	USED(o);
}

int
read_MathValueRecord(Otf *o, MathValueRecord *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 4)) == nil)
		goto err;
	v->value = b[0]<<8 | b[1];
	v->deviceOffset = b[2]<<8 | b[3];
	return 0;
err:
	werrstr("%s: %r", "MathValueRecord");
	return -1;
}

void
print_MathValueRecord(Otfile *f, int indent, Otf *o, MathValueRecord *v)
{
	f->print(f->aux, "%*s%s: %d\n", indent, "", "value", v->value);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "deviceOffset", v->deviceOffset);
	USED(o);
}

int
read_MathConstants(Otf *o, MathConstants *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 8)) == nil)
		goto err;
	v->scriptPercentScaleDown = b[0]<<8 | b[1];
	v->scriptScriptPercentScaleDown = b[2]<<8 | b[3];
	v->delimitedSubFormulaMinHeight = b[4]<<8 | b[5];
	v->displayOperatorMinHeight = b[6]<<8 | b[7];
	if(read_MathValueRecord(o, &v->mathLeading) < 0){
		werrstr("%s: %r", "mathLeading");
		goto err;
	}
	if(read_MathValueRecord(o, &v->axisHeight) < 0){
		werrstr("%s: %r", "axisHeight");
		goto err;
	}
	if(read_MathValueRecord(o, &v->accentBaseHeight) < 0){
		werrstr("%s: %r", "accentBaseHeight");
		goto err;
	}
	if(read_MathValueRecord(o, &v->flattenedAccentBaseHeight) < 0){
		werrstr("%s: %r", "flattenedAccentBaseHeight");
		goto err;
	}
	if(read_MathValueRecord(o, &v->subscriptShiftDown) < 0){
		werrstr("%s: %r", "subscriptShiftDown");
		goto err;
	}
	if(read_MathValueRecord(o, &v->subscriptTopMax) < 0){
		werrstr("%s: %r", "subscriptTopMax");
		goto err;
	}
	if(read_MathValueRecord(o, &v->subscriptBaselineDropMin) < 0){
		werrstr("%s: %r", "subscriptBaselineDropMin");
		goto err;
	}
	if(read_MathValueRecord(o, &v->superscriptShiftUp) < 0){
		werrstr("%s: %r", "superscriptShiftUp");
		goto err;
	}
	if(read_MathValueRecord(o, &v->superscriptShiftUpCramped) < 0){
		werrstr("%s: %r", "superscriptShiftUpCramped");
		goto err;
	}
	if(read_MathValueRecord(o, &v->superscriptBottomMin) < 0){
		werrstr("%s: %r", "superscriptBottomMin");
		goto err;
	}
	if(read_MathValueRecord(o, &v->superscriptBaselineDropMax) < 0){
		werrstr("%s: %r", "superscriptBaselineDropMax");
		goto err;
	}
	if(read_MathValueRecord(o, &v->subSuperscriptGapMin) < 0){
		werrstr("%s: %r", "subSuperscriptGapMin");
		goto err;
	}
	if(read_MathValueRecord(o, &v->superscriptBottomMaxWithSubscript) < 0){
		werrstr("%s: %r", "superscriptBottomMaxWithSubscript");
		goto err;
	}
	if(read_MathValueRecord(o, &v->spaceAfterScript) < 0){
		werrstr("%s: %r", "spaceAfterScript");
		goto err;
	}
	if(read_MathValueRecord(o, &v->upperLimitGapMin) < 0){
		werrstr("%s: %r", "upperLimitGapMin");
		goto err;
	}
	if(read_MathValueRecord(o, &v->upperLimitBaselineRiseMin) < 0){
		werrstr("%s: %r", "upperLimitBaselineRiseMin");
		goto err;
	}
	if(read_MathValueRecord(o, &v->lowerLimitGapMin) < 0){
		werrstr("%s: %r", "lowerLimitGapMin");
		goto err;
	}
	if(read_MathValueRecord(o, &v->lowerLimitBaselineDropMin) < 0){
		werrstr("%s: %r", "lowerLimitBaselineDropMin");
		goto err;
	}
	if(read_MathValueRecord(o, &v->stackTopShiftUp) < 0){
		werrstr("%s: %r", "stackTopShiftUp");
		goto err;
	}
	if(read_MathValueRecord(o, &v->stackTopDisplayStyleShiftUp) < 0){
		werrstr("%s: %r", "stackTopDisplayStyleShiftUp");
		goto err;
	}
	if(read_MathValueRecord(o, &v->stackBottomShiftDown) < 0){
		werrstr("%s: %r", "stackBottomShiftDown");
		goto err;
	}
	if(read_MathValueRecord(o, &v->stackBottomDisplayStyleShiftDown) < 0){
		werrstr("%s: %r", "stackBottomDisplayStyleShiftDown");
		goto err;
	}
	if(read_MathValueRecord(o, &v->stackGapMin) < 0){
		werrstr("%s: %r", "stackGapMin");
		goto err;
	}
	if(read_MathValueRecord(o, &v->stackDisplayStyleGapMin) < 0){
		werrstr("%s: %r", "stackDisplayStyleGapMin");
		goto err;
	}
	if(read_MathValueRecord(o, &v->stretchStackTopShiftUp) < 0){
		werrstr("%s: %r", "stretchStackTopShiftUp");
		goto err;
	}
	if(read_MathValueRecord(o, &v->stretchStackBottomShiftDown) < 0){
		werrstr("%s: %r", "stretchStackBottomShiftDown");
		goto err;
	}
	if(read_MathValueRecord(o, &v->stretchStackGapAboveMin) < 0){
		werrstr("%s: %r", "stretchStackGapAboveMin");
		goto err;
	}
	if(read_MathValueRecord(o, &v->stretchStackGapBelowMin) < 0){
		werrstr("%s: %r", "stretchStackGapBelowMin");
		goto err;
	}
	if(read_MathValueRecord(o, &v->fractionNumeratorShiftUp) < 0){
		werrstr("%s: %r", "fractionNumeratorShiftUp");
		goto err;
	}
	if(read_MathValueRecord(o, &v->fractionNumeratorDisplayStyleShiftUp) < 0){
		werrstr("%s: %r", "fractionNumeratorDisplayStyleShiftUp");
		goto err;
	}
	if(read_MathValueRecord(o, &v->fractionDenominatorShiftDown) < 0){
		werrstr("%s: %r", "fractionDenominatorShiftDown");
		goto err;
	}
	if(read_MathValueRecord(o, &v->fractionDenominatorDisplayStyleShiftDown) < 0){
		werrstr("%s: %r", "fractionDenominatorDisplayStyleShiftDown");
		goto err;
	}
	if(read_MathValueRecord(o, &v->fractionNumeratorGapMin) < 0){
		werrstr("%s: %r", "fractionNumeratorGapMin");
		goto err;
	}
	if(read_MathValueRecord(o, &v->fractionNumDisplayStyleGapMin) < 0){
		werrstr("%s: %r", "fractionNumDisplayStyleGapMin");
		goto err;
	}
	if(read_MathValueRecord(o, &v->fractionRuleThickness) < 0){
		werrstr("%s: %r", "fractionRuleThickness");
		goto err;
	}
	if(read_MathValueRecord(o, &v->fractionDenominatorGapMin) < 0){
		werrstr("%s: %r", "fractionDenominatorGapMin");
		goto err;
	}
	if(read_MathValueRecord(o, &v->fractionDenomDisplayStyleGapMin) < 0){
		werrstr("%s: %r", "fractionDenomDisplayStyleGapMin");
		goto err;
	}
	if(read_MathValueRecord(o, &v->skewedFractionHorizontalGap) < 0){
		werrstr("%s: %r", "skewedFractionHorizontalGap");
		goto err;
	}
	if(read_MathValueRecord(o, &v->skewedFractionVerticalGap) < 0){
		werrstr("%s: %r", "skewedFractionVerticalGap");
		goto err;
	}
	if(read_MathValueRecord(o, &v->overbarVerticalGap) < 0){
		werrstr("%s: %r", "overbarVerticalGap");
		goto err;
	}
	if(read_MathValueRecord(o, &v->overbarRuleThickness) < 0){
		werrstr("%s: %r", "overbarRuleThickness");
		goto err;
	}
	if(read_MathValueRecord(o, &v->overbarExtraAscender) < 0){
		werrstr("%s: %r", "overbarExtraAscender");
		goto err;
	}
	if(read_MathValueRecord(o, &v->underbarVerticalGap) < 0){
		werrstr("%s: %r", "underbarVerticalGap");
		goto err;
	}
	if(read_MathValueRecord(o, &v->underbarRuleThickness) < 0){
		werrstr("%s: %r", "underbarRuleThickness");
		goto err;
	}
	if(read_MathValueRecord(o, &v->underbarExtraDescender) < 0){
		werrstr("%s: %r", "underbarExtraDescender");
		goto err;
	}
	if(read_MathValueRecord(o, &v->radicalVerticalGap) < 0){
		werrstr("%s: %r", "radicalVerticalGap");
		goto err;
	}
	if(read_MathValueRecord(o, &v->radicalDisplayStyleVerticalGap) < 0){
		werrstr("%s: %r", "radicalDisplayStyleVerticalGap");
		goto err;
	}
	if(read_MathValueRecord(o, &v->radicalRuleThickness) < 0){
		werrstr("%s: %r", "radicalRuleThickness");
		goto err;
	}
	if(read_MathValueRecord(o, &v->radicalExtraAscender) < 0){
		werrstr("%s: %r", "radicalExtraAscender");
		goto err;
	}
	if(read_MathValueRecord(o, &v->radicalKernBeforeDegree) < 0){
		werrstr("%s: %r", "radicalKernBeforeDegree");
		goto err;
	}
	if(read_MathValueRecord(o, &v->radicalKernAfterDegree) < 0){
		werrstr("%s: %r", "radicalKernAfterDegree");
		goto err;
	}
	if((b = otfreadn(o, 2)) == nil)
		goto err;
	v->radicalDegreeBottomRaisePercent = b[0]<<8 | b[1];
	return 0;
err:
	werrstr("%s: %r", "MathConstants");
	return -1;
}

void
print_MathConstants(Otfile *f, int indent, Otf *o, MathConstants *v)
{
	f->print(f->aux, "%*s%s: %d\n", indent, "", "scriptPercentScaleDown", v->scriptPercentScaleDown);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "scriptScriptPercentScaleDown", v->scriptScriptPercentScaleDown);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "delimitedSubFormulaMinHeight", v->delimitedSubFormulaMinHeight);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "displayOperatorMinHeight", v->displayOperatorMinHeight);
	f->print(f->aux, "%*s%s:\n", indent, "", "mathLeading");
	print_MathValueRecord(f, indent+indentΔ, o, &v->mathLeading);
	f->print(f->aux, "%*s%s:\n", indent, "", "axisHeight");
	print_MathValueRecord(f, indent+indentΔ, o, &v->axisHeight);
	f->print(f->aux, "%*s%s:\n", indent, "", "accentBaseHeight");
	print_MathValueRecord(f, indent+indentΔ, o, &v->accentBaseHeight);
	f->print(f->aux, "%*s%s:\n", indent, "", "flattenedAccentBaseHeight");
	print_MathValueRecord(f, indent+indentΔ, o, &v->flattenedAccentBaseHeight);
	f->print(f->aux, "%*s%s:\n", indent, "", "subscriptShiftDown");
	print_MathValueRecord(f, indent+indentΔ, o, &v->subscriptShiftDown);
	f->print(f->aux, "%*s%s:\n", indent, "", "subscriptTopMax");
	print_MathValueRecord(f, indent+indentΔ, o, &v->subscriptTopMax);
	f->print(f->aux, "%*s%s:\n", indent, "", "subscriptBaselineDropMin");
	print_MathValueRecord(f, indent+indentΔ, o, &v->subscriptBaselineDropMin);
	f->print(f->aux, "%*s%s:\n", indent, "", "superscriptShiftUp");
	print_MathValueRecord(f, indent+indentΔ, o, &v->superscriptShiftUp);
	f->print(f->aux, "%*s%s:\n", indent, "", "superscriptShiftUpCramped");
	print_MathValueRecord(f, indent+indentΔ, o, &v->superscriptShiftUpCramped);
	f->print(f->aux, "%*s%s:\n", indent, "", "superscriptBottomMin");
	print_MathValueRecord(f, indent+indentΔ, o, &v->superscriptBottomMin);
	f->print(f->aux, "%*s%s:\n", indent, "", "superscriptBaselineDropMax");
	print_MathValueRecord(f, indent+indentΔ, o, &v->superscriptBaselineDropMax);
	f->print(f->aux, "%*s%s:\n", indent, "", "subSuperscriptGapMin");
	print_MathValueRecord(f, indent+indentΔ, o, &v->subSuperscriptGapMin);
	f->print(f->aux, "%*s%s:\n", indent, "", "superscriptBottomMaxWithSubscript");
	print_MathValueRecord(f, indent+indentΔ, o, &v->superscriptBottomMaxWithSubscript);
	f->print(f->aux, "%*s%s:\n", indent, "", "spaceAfterScript");
	print_MathValueRecord(f, indent+indentΔ, o, &v->spaceAfterScript);
	f->print(f->aux, "%*s%s:\n", indent, "", "upperLimitGapMin");
	print_MathValueRecord(f, indent+indentΔ, o, &v->upperLimitGapMin);
	f->print(f->aux, "%*s%s:\n", indent, "", "upperLimitBaselineRiseMin");
	print_MathValueRecord(f, indent+indentΔ, o, &v->upperLimitBaselineRiseMin);
	f->print(f->aux, "%*s%s:\n", indent, "", "lowerLimitGapMin");
	print_MathValueRecord(f, indent+indentΔ, o, &v->lowerLimitGapMin);
	f->print(f->aux, "%*s%s:\n", indent, "", "lowerLimitBaselineDropMin");
	print_MathValueRecord(f, indent+indentΔ, o, &v->lowerLimitBaselineDropMin);
	f->print(f->aux, "%*s%s:\n", indent, "", "stackTopShiftUp");
	print_MathValueRecord(f, indent+indentΔ, o, &v->stackTopShiftUp);
	f->print(f->aux, "%*s%s:\n", indent, "", "stackTopDisplayStyleShiftUp");
	print_MathValueRecord(f, indent+indentΔ, o, &v->stackTopDisplayStyleShiftUp);
	f->print(f->aux, "%*s%s:\n", indent, "", "stackBottomShiftDown");
	print_MathValueRecord(f, indent+indentΔ, o, &v->stackBottomShiftDown);
	f->print(f->aux, "%*s%s:\n", indent, "", "stackBottomDisplayStyleShiftDown");
	print_MathValueRecord(f, indent+indentΔ, o, &v->stackBottomDisplayStyleShiftDown);
	f->print(f->aux, "%*s%s:\n", indent, "", "stackGapMin");
	print_MathValueRecord(f, indent+indentΔ, o, &v->stackGapMin);
	f->print(f->aux, "%*s%s:\n", indent, "", "stackDisplayStyleGapMin");
	print_MathValueRecord(f, indent+indentΔ, o, &v->stackDisplayStyleGapMin);
	f->print(f->aux, "%*s%s:\n", indent, "", "stretchStackTopShiftUp");
	print_MathValueRecord(f, indent+indentΔ, o, &v->stretchStackTopShiftUp);
	f->print(f->aux, "%*s%s:\n", indent, "", "stretchStackBottomShiftDown");
	print_MathValueRecord(f, indent+indentΔ, o, &v->stretchStackBottomShiftDown);
	f->print(f->aux, "%*s%s:\n", indent, "", "stretchStackGapAboveMin");
	print_MathValueRecord(f, indent+indentΔ, o, &v->stretchStackGapAboveMin);
	f->print(f->aux, "%*s%s:\n", indent, "", "stretchStackGapBelowMin");
	print_MathValueRecord(f, indent+indentΔ, o, &v->stretchStackGapBelowMin);
	f->print(f->aux, "%*s%s:\n", indent, "", "fractionNumeratorShiftUp");
	print_MathValueRecord(f, indent+indentΔ, o, &v->fractionNumeratorShiftUp);
	f->print(f->aux, "%*s%s:\n", indent, "", "fractionNumeratorDisplayStyleShiftUp");
	print_MathValueRecord(f, indent+indentΔ, o, &v->fractionNumeratorDisplayStyleShiftUp);
	f->print(f->aux, "%*s%s:\n", indent, "", "fractionDenominatorShiftDown");
	print_MathValueRecord(f, indent+indentΔ, o, &v->fractionDenominatorShiftDown);
	f->print(f->aux, "%*s%s:\n", indent, "", "fractionDenominatorDisplayStyleShiftDown");
	print_MathValueRecord(f, indent+indentΔ, o, &v->fractionDenominatorDisplayStyleShiftDown);
	f->print(f->aux, "%*s%s:\n", indent, "", "fractionNumeratorGapMin");
	print_MathValueRecord(f, indent+indentΔ, o, &v->fractionNumeratorGapMin);
	f->print(f->aux, "%*s%s:\n", indent, "", "fractionNumDisplayStyleGapMin");
	print_MathValueRecord(f, indent+indentΔ, o, &v->fractionNumDisplayStyleGapMin);
	f->print(f->aux, "%*s%s:\n", indent, "", "fractionRuleThickness");
	print_MathValueRecord(f, indent+indentΔ, o, &v->fractionRuleThickness);
	f->print(f->aux, "%*s%s:\n", indent, "", "fractionDenominatorGapMin");
	print_MathValueRecord(f, indent+indentΔ, o, &v->fractionDenominatorGapMin);
	f->print(f->aux, "%*s%s:\n", indent, "", "fractionDenomDisplayStyleGapMin");
	print_MathValueRecord(f, indent+indentΔ, o, &v->fractionDenomDisplayStyleGapMin);
	f->print(f->aux, "%*s%s:\n", indent, "", "skewedFractionHorizontalGap");
	print_MathValueRecord(f, indent+indentΔ, o, &v->skewedFractionHorizontalGap);
	f->print(f->aux, "%*s%s:\n", indent, "", "skewedFractionVerticalGap");
	print_MathValueRecord(f, indent+indentΔ, o, &v->skewedFractionVerticalGap);
	f->print(f->aux, "%*s%s:\n", indent, "", "overbarVerticalGap");
	print_MathValueRecord(f, indent+indentΔ, o, &v->overbarVerticalGap);
	f->print(f->aux, "%*s%s:\n", indent, "", "overbarRuleThickness");
	print_MathValueRecord(f, indent+indentΔ, o, &v->overbarRuleThickness);
	f->print(f->aux, "%*s%s:\n", indent, "", "overbarExtraAscender");
	print_MathValueRecord(f, indent+indentΔ, o, &v->overbarExtraAscender);
	f->print(f->aux, "%*s%s:\n", indent, "", "underbarVerticalGap");
	print_MathValueRecord(f, indent+indentΔ, o, &v->underbarVerticalGap);
	f->print(f->aux, "%*s%s:\n", indent, "", "underbarRuleThickness");
	print_MathValueRecord(f, indent+indentΔ, o, &v->underbarRuleThickness);
	f->print(f->aux, "%*s%s:\n", indent, "", "underbarExtraDescender");
	print_MathValueRecord(f, indent+indentΔ, o, &v->underbarExtraDescender);
	f->print(f->aux, "%*s%s:\n", indent, "", "radicalVerticalGap");
	print_MathValueRecord(f, indent+indentΔ, o, &v->radicalVerticalGap);
	f->print(f->aux, "%*s%s:\n", indent, "", "radicalDisplayStyleVerticalGap");
	print_MathValueRecord(f, indent+indentΔ, o, &v->radicalDisplayStyleVerticalGap);
	f->print(f->aux, "%*s%s:\n", indent, "", "radicalRuleThickness");
	print_MathValueRecord(f, indent+indentΔ, o, &v->radicalRuleThickness);
	f->print(f->aux, "%*s%s:\n", indent, "", "radicalExtraAscender");
	print_MathValueRecord(f, indent+indentΔ, o, &v->radicalExtraAscender);
	f->print(f->aux, "%*s%s:\n", indent, "", "radicalKernBeforeDegree");
	print_MathValueRecord(f, indent+indentΔ, o, &v->radicalKernBeforeDegree);
	f->print(f->aux, "%*s%s:\n", indent, "", "radicalKernAfterDegree");
	print_MathValueRecord(f, indent+indentΔ, o, &v->radicalKernAfterDegree);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "radicalDegreeBottomRaisePercent", v->radicalDegreeBottomRaisePercent);
	USED(o);
}

int
read_MathItalicsCorrectionInfo(Otf *o, MathItalicsCorrectionInfo *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 4)) == nil)
		goto err;
	v->italicsCorrectionCoverageOffset = b[0]<<8 | b[1];
	v->italicsCorrectionCount = b[2]<<8 | b[3];
	if(otfarray(o, &v->italicsCorrection, read_MathValueRecord, sizeof(MathValueRecord), v->italicsCorrectionCount) < 0){
		werrstr("%s: %r", "italicsCorrection");
		goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "MathItalicsCorrectionInfo");
	return -1;
}

void
print_MathItalicsCorrectionInfo(Otfile *f, int indent, Otf *o, MathItalicsCorrectionInfo *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "italicsCorrectionCoverageOffset", v->italicsCorrectionCoverageOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "italicsCorrectionCount", v->italicsCorrectionCount);
	for(int i = 0; i < v->italicsCorrectionCount; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "italicsCorrection", i);
		print_MathValueRecord(f, indent+indentΔ, o, &v->italicsCorrection[i]);
	}
	USED(o);
}

int
read_MathTopAccentAttachment(Otf *o, MathTopAccentAttachment *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 4)) == nil)
		goto err;
	v->topAccentCoverageOffset = b[0]<<8 | b[1];
	v->topAccentAttachmentCount = b[2]<<8 | b[3];
	if(otfarray(o, &v->topAccentAttachment, read_MathValueRecord, sizeof(MathValueRecord), v->topAccentAttachmentCount) < 0){
		werrstr("%s: %r", "topAccentAttachment");
		goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "MathTopAccentAttachment");
	return -1;
}

void
print_MathTopAccentAttachment(Otfile *f, int indent, Otf *o, MathTopAccentAttachment *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "topAccentCoverageOffset", v->topAccentCoverageOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "topAccentAttachmentCount", v->topAccentAttachmentCount);
	for(int i = 0; i < v->topAccentAttachmentCount; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "topAccentAttachment", i);
		print_MathValueRecord(f, indent+indentΔ, o, &v->topAccentAttachment[i]);
	}
	USED(o);
}

int
read_MathKernInfoRecord(Otf *o, MathKernInfoRecord *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 8)) == nil)
		goto err;
	v->topRightMathKernOffset = b[0]<<8 | b[1];
	v->topLeftMathKernOffset = b[2]<<8 | b[3];
	v->bottomRightMathKernOffset = b[4]<<8 | b[5];
	v->bottomLeftMathKernOffset = b[6]<<8 | b[7];
	return 0;
err:
	werrstr("%s: %r", "MathKernInfoRecord");
	return -1;
}

void
print_MathKernInfoRecord(Otfile *f, int indent, Otf *o, MathKernInfoRecord *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "topRightMathKernOffset", v->topRightMathKernOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "topLeftMathKernOffset", v->topLeftMathKernOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "bottomRightMathKernOffset", v->bottomRightMathKernOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "bottomLeftMathKernOffset", v->bottomLeftMathKernOffset);
	USED(o);
}

int
read_MathKernInfo(Otf *o, MathKernInfo *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 4)) == nil)
		goto err;
	v->mathKernCoverageOffset = b[0]<<8 | b[1];
	v->mathKernCount = b[2]<<8 | b[3];
	if(otfarray(o, &v->mathKernInfoRecords, read_MathKernInfoRecord, sizeof(MathKernInfoRecord), v->mathKernCount) < 0){
		werrstr("%s: %r", "mathKernInfoRecords");
		goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "MathKernInfo");
	return -1;
}

void
print_MathKernInfo(Otfile *f, int indent, Otf *o, MathKernInfo *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "mathKernCoverageOffset", v->mathKernCoverageOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "mathKernCount", v->mathKernCount);
	for(int i = 0; i < v->mathKernCount; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "mathKernInfoRecords", i);
		print_MathKernInfoRecord(f, indent+indentΔ, o, &v->mathKernInfoRecords[i]);
	}
	USED(o);
}

int
read_MathKern(Otf *o, MathKern *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 2)) == nil)
		goto err;
	v->heightCount = b[0]<<8 | b[1];
	if(otfarray(o, &v->correctionHeight, read_MathValueRecord, sizeof(MathValueRecord), v->heightCount) < 0){
		werrstr("%s: %r", "correctionHeight");
		goto err;
	}
	if(otfarray(o, &v->kernValues, read_MathValueRecord, sizeof(MathValueRecord), v->heightCount) < 0){
		werrstr("%s: %r", "kernValues");
		goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "MathKern");
	return -1;
}

void
print_MathKern(Otfile *f, int indent, Otf *o, MathKern *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "heightCount", v->heightCount);
	for(int i = 0; i < v->heightCount; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "correctionHeight", i);
		print_MathValueRecord(f, indent+indentΔ, o, &v->correctionHeight[i]);
	}
	for(int i = 0; i < v->heightCount; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "kernValues", i);
		print_MathValueRecord(f, indent+indentΔ, o, &v->kernValues[i]);
	}
	USED(o);
}

int
read_Coverage1(Otf *o, Coverage1 *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 2)) == nil)
		goto err;
	v->glyphCount = b[0]<<8 | b[1];
	if((b = otfreadn(o, v->glyphCount*2)) == nil)
		goto err;
	v->glyphArray = malloc(v->glyphCount*sizeof(*v->glyphArray));
	for(int i = 0; i < v->glyphCount; i++)
		v->glyphArray[i] = b[0+i*2]<<8 | b[1+i*2];
	return 0;
err:
	werrstr("%s: %r", "Coverage1");
	return -1;
}

void
print_Coverage1(Otfile *f, int indent, Otf *o, Coverage1 *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "glyphCount", v->glyphCount);
	for(int i = 0; i < v->glyphCount; i++)
		f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "glyphArray", i, v->glyphArray[i]);
	USED(o);
}

int
read_RangeRecord(Otf *o, RangeRecord *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 6)) == nil)
		goto err;
	v->startGlyphID = b[0]<<8 | b[1];
	v->endGlyphID = b[2]<<8 | b[3];
	v->startCoverageIndex = b[4]<<8 | b[5];
	return 0;
err:
	werrstr("%s: %r", "RangeRecord");
	return -1;
}

void
print_RangeRecord(Otfile *f, int indent, Otf *o, RangeRecord *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "startGlyphID", v->startGlyphID);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "endGlyphID", v->endGlyphID);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "startCoverageIndex", v->startCoverageIndex);
	USED(o);
}

int
read_Coverage2(Otf *o, Coverage2 *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 2)) == nil)
		goto err;
	v->rangeCount = b[0]<<8 | b[1];
	if(otfarray(o, &v->rangeRecords, read_RangeRecord, sizeof(RangeRecord), v->rangeCount) < 0){
		werrstr("%s: %r", "rangeRecords");
		goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "Coverage2");
	return -1;
}

void
print_Coverage2(Otfile *f, int indent, Otf *o, Coverage2 *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "rangeCount", v->rangeCount);
	for(int i = 0; i < v->rangeCount; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "rangeRecords", i);
		print_RangeRecord(f, indent+indentΔ, o, &v->rangeRecords[i]);
	}
	USED(o);
}

int
read_Coverage(Otf *o, Coverage *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 2)) == nil)
		goto err;
	v->format = b[0]<<8 | b[1];
	if(v->format != 1 && v->format != 2){
		werrstr("%s: invalid value: %d (%#ux)", "format", v->format, v->format);
		goto err;
	}
	if(v->format == 1){
		if(read_Coverage1(o, &v->cov1) < 0){
			werrstr("%s: %r", "cov1");
			goto err;
		}
	}
	if(v->format == 2){
		if(read_Coverage2(o, &v->cov2) < 0){
			werrstr("%s: %r", "cov2");
			goto err;
		}
	}
	return 0;
err:
	werrstr("%s: %r", "Coverage");
	return -1;
}

void
print_Coverage(Otfile *f, int indent, Otf *o, Coverage *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "format", v->format);
	if(v->format == 1){
		f->print(f->aux, "%*s%s:\n", indent, "", "cov1");
		print_Coverage1(f, indent+indentΔ, o, &v->cov1);
	}
	if(v->format == 2){
		f->print(f->aux, "%*s%s:\n", indent, "", "cov2");
		print_Coverage2(f, indent+indentΔ, o, &v->cov2);
	}
	USED(o);
}

int
read_MathGlyphVariantRecord(Otf *o, MathGlyphVariantRecord *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 4)) == nil)
		goto err;
	v->variantGlyph = b[0]<<8 | b[1];
	v->advanceMeasurement = b[2]<<8 | b[3];
	return 0;
err:
	werrstr("%s: %r", "MathGlyphVariantRecord");
	return -1;
}

void
print_MathGlyphVariantRecord(Otfile *f, int indent, Otf *o, MathGlyphVariantRecord *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "variantGlyph", v->variantGlyph);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "advanceMeasurement", v->advanceMeasurement);
	USED(o);
}

int
read_GlyphPart(Otf *o, GlyphPart *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 10)) == nil)
		goto err;
	v->glyphID = b[0]<<8 | b[1];
	v->startConnectorLength = b[2]<<8 | b[3];
	v->endConnectorLength = b[4]<<8 | b[5];
	v->fullAdvance = b[6]<<8 | b[7];
	v->partFlags = b[8]<<8 | b[9];
	return 0;
err:
	werrstr("%s: %r", "GlyphPart");
	return -1;
}

void
print_GlyphPart(Otfile *f, int indent, Otf *o, GlyphPart *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "glyphID", v->glyphID);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "startConnectorLength", v->startConnectorLength);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "endConnectorLength", v->endConnectorLength);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "fullAdvance", v->fullAdvance);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "partFlags", v->partFlags);
	USED(o);
}

int
read_GlyphAssembly(Otf *o, GlyphAssembly *v)
{
	u8int *b = nil; USED(b);
	if(read_MathValueRecord(o, &v->italicsCorrection) < 0){
		werrstr("%s: %r", "italicsCorrection");
		goto err;
	}
	if((b = otfreadn(o, 2)) == nil)
		goto err;
	v->partCount = b[0]<<8 | b[1];
	if(otfarray(o, &v->partRecords, read_GlyphPart, sizeof(GlyphPart), v->partCount) < 0){
		werrstr("%s: %r", "partRecords");
		goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "GlyphAssembly");
	return -1;
}

void
print_GlyphAssembly(Otfile *f, int indent, Otf *o, GlyphAssembly *v)
{
	f->print(f->aux, "%*s%s:\n", indent, "", "italicsCorrection");
	print_MathValueRecord(f, indent+indentΔ, o, &v->italicsCorrection);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "partCount", v->partCount);
	for(int i = 0; i < v->partCount; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "partRecords", i);
		print_GlyphPart(f, indent+indentΔ, o, &v->partRecords[i]);
	}
	USED(o);
}

int
read_MathGlyphConstruction(Otf *o, MathGlyphConstruction *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 4)) == nil)
		goto err;
	v->glyphAssemblyOffset = b[0]<<8 | b[1];
	v->variantCount = b[2]<<8 | b[3];
	if(otfarray(o, &v->mathGlyphVariantRecords, read_MathGlyphVariantRecord, sizeof(MathGlyphVariantRecord), v->variantCount) < 0){
		werrstr("%s: %r", "mathGlyphVariantRecords");
		goto err;
	}
	if(v->glyphAssemblyOffset != 0){
		if(otfpushrange(o, v->glyphAssemblyOffset, -1) < 0)
			goto err;
		v->glyphAssembly = calloc(1, sizeof(*v->glyphAssembly));
		if(read_GlyphAssembly(o, v->glyphAssembly) < 0){
			werrstr("%s: %r", "glyphAssembly");
			goto err;
		}
		if(otfpoprange(o) < 0)
			goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "MathGlyphConstruction");
	return -1;
}

void
print_MathGlyphConstruction(Otfile *f, int indent, Otf *o, MathGlyphConstruction *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "glyphAssemblyOffset", v->glyphAssemblyOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "variantCount", v->variantCount);
	for(int i = 0; i < v->variantCount; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "mathGlyphVariantRecords", i);
		print_MathGlyphVariantRecord(f, indent+indentΔ, o, &v->mathGlyphVariantRecords[i]);
	}
	f->print(f->aux, "%*s%s:\n", indent, "", "glyphAssembly");
	if(v->glyphAssembly != nil)
		print_GlyphAssembly(f, indent+indentΔ, o, v->glyphAssembly);
	USED(o);
}

int
read_MathVariants(Otf *o, MathVariants *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 10)) == nil)
		goto err;
	v->minConnectorOverlap = b[0]<<8 | b[1];
	v->vertGlyphCoverageOffset = b[2]<<8 | b[3];
	v->horizGlyphCoverageOffset = b[4]<<8 | b[5];
	v->vertGlyphCount = b[6]<<8 | b[7];
	v->horizGlyphCount = b[8]<<8 | b[9];
	if((b = otfreadn(o, v->vertGlyphCount*2)) == nil)
		goto err;
	v->vertGlyphConstructionOffsets = malloc(v->vertGlyphCount*sizeof(*v->vertGlyphConstructionOffsets));
	for(int i = 0; i < v->vertGlyphCount; i++)
		v->vertGlyphConstructionOffsets[i] = b[0+i*2]<<8 | b[1+i*2];
	if((b = otfreadn(o, v->horizGlyphCount*2)) == nil)
		goto err;
	v->horizGlyphConstructionOffsets = malloc(v->horizGlyphCount*sizeof(*v->horizGlyphConstructionOffsets));
	for(int i = 0; i < v->horizGlyphCount; i++)
		v->horizGlyphConstructionOffsets[i] = b[0+i*2]<<8 | b[1+i*2];
	if(v->vertGlyphCoverageOffset != 0){
		if(otfpushrange(o, v->vertGlyphCoverageOffset, -1) < 0)
			goto err;
		v->vertGlyphCoverage = calloc(1, sizeof(*v->vertGlyphCoverage));
		if(read_Coverage(o, v->vertGlyphCoverage) < 0){
			werrstr("%s: %r", "vertGlyphCoverage");
			goto err;
		}
		if(otfpoprange(o) < 0)
			goto err;
	}
	if(v->horizGlyphCoverageOffset != 0){
		if(otfpushrange(o, v->horizGlyphCoverageOffset, -1) < 0)
			goto err;
		v->horizGlyphCoverage = calloc(1, sizeof(*v->horizGlyphCoverage));
		if(read_Coverage(o, v->horizGlyphCoverage) < 0){
			werrstr("%s: %r", "horizGlyphCoverage");
			goto err;
		}
		if(otfpoprange(o) < 0)
			goto err;
	}
	if(v->vertGlyphCount > 0){
		v->vertGlyphConstruction = calloc(v->vertGlyphCount, sizeof(*v->vertGlyphConstruction));
		for(int i = 0; i < v->vertGlyphCount; i++){
			if(v->vertGlyphConstructionOffsets[i] == 0)
				continue;
			if(otfpushrange(o, v->vertGlyphConstructionOffsets[i], -1))
				goto err;
			int r = read_MathGlyphConstruction(o, v->vertGlyphConstruction+i);
			if(otfpoprange(o) < 0)
				goto err;
			if(r < 0){
				memset(v->vertGlyphConstruction+i, 0, sizeof(*v->vertGlyphConstruction));
				break;
			}
		}
	}
	if(v->horizGlyphCount > 0){
		v->horizGlyphConstruction = calloc(v->horizGlyphCount, sizeof(*v->horizGlyphConstruction));
		for(int i = 0; i < v->horizGlyphCount; i++){
			if(v->horizGlyphConstructionOffsets[i] == 0)
				continue;
			if(otfpushrange(o, v->horizGlyphConstructionOffsets[i], -1))
				goto err;
			int r = read_MathGlyphConstruction(o, v->horizGlyphConstruction+i);
			if(otfpoprange(o) < 0)
				goto err;
			if(r < 0){
				memset(v->horizGlyphConstruction+i, 0, sizeof(*v->horizGlyphConstruction));
				break;
			}
		}
	}
	return 0;
err:
	werrstr("%s: %r", "MathVariants");
	return -1;
}

void
print_MathVariants(Otfile *f, int indent, Otf *o, MathVariants *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "minConnectorOverlap", v->minConnectorOverlap);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "vertGlyphCoverageOffset", v->vertGlyphCoverageOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "horizGlyphCoverageOffset", v->horizGlyphCoverageOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "vertGlyphCount", v->vertGlyphCount);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "horizGlyphCount", v->horizGlyphCount);
	for(int i = 0; i < v->vertGlyphCount; i++)
		f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "vertGlyphConstructionOffsets", i, v->vertGlyphConstructionOffsets[i]);
	for(int i = 0; i < v->horizGlyphCount; i++)
		f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "horizGlyphConstructionOffsets", i, v->horizGlyphConstructionOffsets[i]);
	f->print(f->aux, "%*s%s:\n", indent, "", "vertGlyphCoverage");
	if(v->vertGlyphCoverage != nil)
		print_Coverage(f, indent+indentΔ, o, v->vertGlyphCoverage);
	f->print(f->aux, "%*s%s:\n", indent, "", "horizGlyphCoverage");
	if(v->horizGlyphCoverage != nil)
		print_Coverage(f, indent+indentΔ, o, v->horizGlyphCoverage);
	for(int i = 0; i < v->vertGlyphCount; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "vertGlyphConstruction", i);
		print_MathGlyphConstruction(f, indent+indentΔ, o, &v->vertGlyphConstruction[i]);
	}
	for(int i = 0; i < v->horizGlyphCount; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "horizGlyphConstruction", i);
		print_MathGlyphConstruction(f, indent+indentΔ, o, &v->horizGlyphConstruction[i]);
	}
	USED(o);
}

int
read_MathGlyphInfo(Otf *o, MathGlyphInfo *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 8)) == nil)
		goto err;
	v->mathItalicsCorrectionInfoOffset = b[0]<<8 | b[1];
	v->mathTopAccentAttachmentOffset = b[2]<<8 | b[3];
	v->extendedShapeCoverageOffset = b[4]<<8 | b[5];
	v->mathKernInfoOffset = b[6]<<8 | b[7];
	if(v->mathItalicsCorrectionInfoOffset != 0){
		if(otfpushrange(o, v->mathItalicsCorrectionInfoOffset, -1) < 0)
			goto err;
		v->mathItalicsCorrectionInfo = calloc(1, sizeof(*v->mathItalicsCorrectionInfo));
		if(read_MathItalicsCorrectionInfo(o, v->mathItalicsCorrectionInfo) < 0){
			werrstr("%s: %r", "mathItalicsCorrectionInfo");
			goto err;
		}
		if(otfpoprange(o) < 0)
			goto err;
	}
	if(v->mathTopAccentAttachmentOffset != 0){
		if(otfpushrange(o, v->mathTopAccentAttachmentOffset, -1) < 0)
			goto err;
		v->mathTopAccentAttachment = calloc(1, sizeof(*v->mathTopAccentAttachment));
		if(read_MathTopAccentAttachment(o, v->mathTopAccentAttachment) < 0){
			werrstr("%s: %r", "mathTopAccentAttachment");
			goto err;
		}
		if(otfpoprange(o) < 0)
			goto err;
	}
	if(v->mathKernInfoOffset != 0){
		if(otfpushrange(o, v->mathKernInfoOffset, -1) < 0)
			goto err;
		v->mathKernInfo = calloc(1, sizeof(*v->mathKernInfo));
		if(read_MathKernInfo(o, v->mathKernInfo) < 0){
			werrstr("%s: %r", "mathKernInfo");
			goto err;
		}
		if(otfpoprange(o) < 0)
			goto err;
	}
	if(v->extendedShapeCoverageOffset != 0){
		if(otfpushrange(o, v->extendedShapeCoverageOffset, -1) < 0)
			goto err;
		v->extendedShapeCoverage = calloc(1, sizeof(*v->extendedShapeCoverage));
		if(read_Coverage(o, v->extendedShapeCoverage) < 0){
			werrstr("%s: %r", "extendedShapeCoverage");
			goto err;
		}
		if(otfpoprange(o) < 0)
			goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "MathGlyphInfo");
	return -1;
}

void
print_MathGlyphInfo(Otfile *f, int indent, Otf *o, MathGlyphInfo *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "mathItalicsCorrectionInfoOffset", v->mathItalicsCorrectionInfoOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "mathTopAccentAttachmentOffset", v->mathTopAccentAttachmentOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "extendedShapeCoverageOffset", v->extendedShapeCoverageOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "mathKernInfoOffset", v->mathKernInfoOffset);
	f->print(f->aux, "%*s%s:\n", indent, "", "mathItalicsCorrectionInfo");
	if(v->mathItalicsCorrectionInfo != nil)
		print_MathItalicsCorrectionInfo(f, indent+indentΔ, o, v->mathItalicsCorrectionInfo);
	f->print(f->aux, "%*s%s:\n", indent, "", "mathTopAccentAttachment");
	if(v->mathTopAccentAttachment != nil)
		print_MathTopAccentAttachment(f, indent+indentΔ, o, v->mathTopAccentAttachment);
	f->print(f->aux, "%*s%s:\n", indent, "", "mathKernInfo");
	if(v->mathKernInfo != nil)
		print_MathKernInfo(f, indent+indentΔ, o, v->mathKernInfo);
	f->print(f->aux, "%*s%s:\n", indent, "", "extendedShapeCoverage");
	if(v->extendedShapeCoverage != nil)
		print_Coverage(f, indent+indentΔ, o, v->extendedShapeCoverage);
	USED(o);
}

int
read_TableMATH(Otf *o, TableMATH *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 10)) == nil)
		goto err;
	u16int majorVersion = b[0]<<8 | b[1];
	if(majorVersion != 1){
		werrstr("%s: invalid value: %d (%#ux)", "majorVersion", majorVersion, majorVersion);
		goto err;
	}
	u16int minorVersion = b[2]<<8 | b[3];
	if(minorVersion != 0){
		werrstr("%s: invalid value: %d (%#ux)", "minorVersion", minorVersion, minorVersion);
		goto err;
	}
	v->mathConstantsOffset = b[4]<<8 | b[5];
	v->mathGlyphInfoOffset = b[6]<<8 | b[7];
	v->mathVariantsOffset = b[8]<<8 | b[9];
	if(v->mathConstantsOffset != 0){
		if(otfpushrange(o, v->mathConstantsOffset, -1) < 0)
			goto err;
		v->mathConstants = calloc(1, sizeof(*v->mathConstants));
		if(read_MathConstants(o, v->mathConstants) < 0){
			werrstr("%s: %r", "mathConstants");
			goto err;
		}
		if(otfpoprange(o) < 0)
			goto err;
	}
	if(v->mathGlyphInfoOffset != 0){
		if(otfpushrange(o, v->mathGlyphInfoOffset, -1) < 0)
			goto err;
		v->mathGlyphInfo = calloc(1, sizeof(*v->mathGlyphInfo));
		if(read_MathGlyphInfo(o, v->mathGlyphInfo) < 0){
			werrstr("%s: %r", "mathGlyphInfo");
			goto err;
		}
		if(otfpoprange(o) < 0)
			goto err;
	}
	if(v->mathVariantsOffset != 0){
		if(otfpushrange(o, v->mathVariantsOffset, -1) < 0)
			goto err;
		v->mathVariants = calloc(1, sizeof(*v->mathVariants));
		if(read_MathVariants(o, v->mathVariants) < 0){
			werrstr("%s: %r", "mathVariants");
			goto err;
		}
		if(otfpoprange(o) < 0)
			goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "TableMATH");
	return -1;
}

void
print_TableMATH(Otfile *f, int indent, Otf *o, TableMATH *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "mathConstantsOffset", v->mathConstantsOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "mathGlyphInfoOffset", v->mathGlyphInfoOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "mathVariantsOffset", v->mathVariantsOffset);
	f->print(f->aux, "%*s%s:\n", indent, "", "mathConstants");
	if(v->mathConstants != nil)
		print_MathConstants(f, indent+indentΔ, o, v->mathConstants);
	f->print(f->aux, "%*s%s:\n", indent, "", "mathGlyphInfo");
	if(v->mathGlyphInfo != nil)
		print_MathGlyphInfo(f, indent+indentΔ, o, v->mathGlyphInfo);
	f->print(f->aux, "%*s%s:\n", indent, "", "mathVariants");
	if(v->mathVariants != nil)
		print_MathVariants(f, indent+indentΔ, o, v->mathVariants);
	USED(o);
}

int
read_KernPair(Otf *o, KernPair *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 6)) == nil)
		goto err;
	v->left = b[0]<<8 | b[1];
	v->right = b[2]<<8 | b[3];
	v->value = b[4]<<8 | b[5];
	return 0;
err:
	werrstr("%s: %r", "KernPair");
	return -1;
}

void
print_KernPair(Otfile *f, int indent, Otf *o, KernPair *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "left", v->left);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "right", v->right);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "value", v->value);
	USED(o);
}

int
read_KernSubtable0(Otf *o, KernSubtable0 *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 8)) == nil)
		goto err;
	v->nPairs = b[0]<<8 | b[1];
	v->searchRange = b[2]<<8 | b[3];
	v->entrySelector = b[4]<<8 | b[5];
	v->rangeShift = b[6]<<8 | b[7];
	if(otfarray(o, &v->kernPairs, read_KernPair, sizeof(KernPair), v->nPairs) < 0){
		werrstr("%s: %r", "kernPairs");
		goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "KernSubtable0");
	return -1;
}

void
print_KernSubtable0(Otfile *f, int indent, Otf *o, KernSubtable0 *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "nPairs", v->nPairs);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "searchRange", v->searchRange);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "entrySelector", v->entrySelector);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "rangeShift", v->rangeShift);
	for(int i = 0; i < v->nPairs; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "kernPairs", i);
		print_KernPair(f, indent+indentΔ, o, &v->kernPairs[i]);
	}
	USED(o);
}

int
read_KernClass(Otf *o, KernClass *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 4)) == nil)
		goto err;
	v->firstGlyph = b[0]<<8 | b[1];
	v->nGlyphs = b[2]<<8 | b[3];
	if((b = otfreadn(o, v->nGlyphs*2)) == nil)
		goto err;
	v->values = malloc(v->nGlyphs*sizeof(*v->values));
	for(int i = 0; i < v->nGlyphs; i++)
		v->values[i] = b[0+i*2]<<8 | b[1+i*2];
	return 0;
err:
	werrstr("%s: %r", "KernClass");
	return -1;
}

void
print_KernClass(Otfile *f, int indent, Otf *o, KernClass *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "firstGlyph", v->firstGlyph);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "nGlyphs", v->nGlyphs);
	for(int i = 0; i < v->nGlyphs; i++)
		f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "values", i, v->values[i]);
	USED(o);
}

int
read_KernSubtable2(Otf *o, KernSubtable2 *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 8)) == nil)
		goto err;
	v->rowWidth = b[0]<<8 | b[1];
	v->leftClassOffset = b[2]<<8 | b[3];
	v->rightClassOffset = b[4]<<8 | b[5];
	v->kerningArrayOffset = b[6]<<8 | b[7];
	if(v->leftClassOffset != 0){
		if(otfpushrange(o, v->leftClassOffset, -1) < 0)
			goto err;
		v->leftClass = calloc(1, sizeof(*v->leftClass));
		if(read_KernClass(o, v->leftClass) < 0){
			werrstr("%s: %r", "leftClass");
			goto err;
		}
		if(otfpoprange(o) < 0)
			goto err;
	}
	if(v->rightClassOffset != 0){
		if(otfpushrange(o, v->rightClassOffset, -1) < 0)
			goto err;
		v->rightClass = calloc(1, sizeof(*v->rightClass));
		if(read_KernClass(o, v->rightClass) < 0){
			werrstr("%s: %r", "rightClass");
			goto err;
		}
		if(otfpoprange(o) < 0)
			goto err;
	}
	if((b = otfreadn(o, (v->rowWidth/2)*2)) == nil)
		goto err;
	v->kerningArray = malloc((v->rowWidth/2)*sizeof(*v->kerningArray));
	for(int i = 0; i < (v->rowWidth/2); i++)
		v->kerningArray[i] = b[0+i*2]<<8 | b[1+i*2];
	return 0;
err:
	werrstr("%s: %r", "KernSubtable2");
	return -1;
}

void
print_KernSubtable2(Otfile *f, int indent, Otf *o, KernSubtable2 *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "rowWidth", v->rowWidth);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "leftClassOffset", v->leftClassOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "rightClassOffset", v->rightClassOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "kerningArrayOffset", v->kerningArrayOffset);
	f->print(f->aux, "%*s%s:\n", indent, "", "leftClass");
	if(v->leftClass != nil)
		print_KernClass(f, indent+indentΔ, o, v->leftClass);
	f->print(f->aux, "%*s%s:\n", indent, "", "rightClass");
	if(v->rightClass != nil)
		print_KernClass(f, indent+indentΔ, o, v->rightClass);
	for(int i = 0; i < (v->rowWidth/2); i++)
		f->print(f->aux, "%*s%s[%d]: %d\n", indent, "", "kerningArray", i, v->kerningArray[i]);
	USED(o);
}

int
read_KernSubtable(Otf *o, KernSubtable *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 6)) == nil)
		goto err;
	u16int version = b[0]<<8 | b[1];
	if(version != 0){
		werrstr("%s: invalid value: %d (%#ux)", "version", version, version);
		goto err;
	}
	v->length = b[2]<<8 | b[3];
	if(v->length < 6){
		werrstr("%s: invalid value: %d (%#ux)", "length", v->length, v->length);
		goto err;
	}
	v->coverage = b[4]<<8 | b[5];
	if((v->coverage>>8) == 0){
		if(read_KernSubtable0(o, &v->sub0) < 0){
			werrstr("%s: %r", "sub0");
			goto err;
		}
	}
	if((v->coverage>>8) == 2){
		if(read_KernSubtable2(o, &v->sub2) < 0){
			werrstr("%s: %r", "sub2");
			goto err;
		}
	}
	return 0;
err:
	werrstr("%s: %r", "KernSubtable");
	return -1;
}

void
print_KernSubtable(Otfile *f, int indent, Otf *o, KernSubtable *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "length", v->length);
	f->print(f->aux, "%*s%s: %#ux%s%s%s%s\n", indent, "", "coverage", v->coverage, (v->coverage&KERN_FL_HORIZONTAL)?" KERN_FL_HORIZONTAL":"", (v->coverage&KERN_FL_MINIMUM)?" KERN_FL_MINIMUM":"", (v->coverage&KERN_FL_CROSS_STREAM)?" KERN_FL_CROSS_STREAM":"", (v->coverage&KERN_FL_OVERRIDE)?" KERN_FL_OVERRIDE":"");
	if((v->coverage>>8) == 0){
		f->print(f->aux, "%*s%s:\n", indent, "", "sub0");
		print_KernSubtable0(f, indent+indentΔ, o, &v->sub0);
	}
	if((v->coverage>>8) == 2){
		f->print(f->aux, "%*s%s:\n", indent, "", "sub2");
		print_KernSubtable2(f, indent+indentΔ, o, &v->sub2);
	}
	USED(o);
}

int
read_TableKern(Otf *o, TableKern *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 4)) == nil)
		goto err;
	u16int version = b[0]<<8 | b[1];
	if(version != 0){
		werrstr("%s: invalid value: %d (%#ux)", "version", version, version);
		goto err;
	}
	v->nTables = b[2]<<8 | b[3];
	if(otfarray(o, &v->subtables, read_KernSubtable, sizeof(KernSubtable), v->nTables) < 0){
		werrstr("%s: %r", "subtables");
		goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "TableKern");
	return -1;
}

void
print_TableKern(Otfile *f, int indent, Otf *o, TableKern *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "nTables", v->nTables);
	for(int i = 0; i < v->nTables; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "subtables", i);
		print_KernSubtable(f, indent+indentΔ, o, &v->subtables[i]);
	}
	USED(o);
}

int
read_TableLoca(Otf *o, TableLoca *v)
{
	u8int *b = nil; USED(b);
	if(o->indexToLocFormat == 0){
		if((b = otfreadn(o, (o->numGlyphs+1)*2)) == nil)
			goto err;
		v->shortOffsets = malloc((o->numGlyphs+1)*sizeof(*v->shortOffsets));
		for(int i = 0; i < (o->numGlyphs+1); i++)
			v->shortOffsets[i] = b[0+i*2]<<8 | b[1+i*2];
	}
	if(o->indexToLocFormat == 1){
		if((b = otfreadn(o, (o->numGlyphs+1)*4)) == nil)
			goto err;
		v->longOffsets = malloc((o->numGlyphs+1)*sizeof(*v->longOffsets));
		for(int i = 0; i < (o->numGlyphs+1); i++)
			v->longOffsets[i] = b[0+i*4]<<24 | b[1+i*4]<<16 | b[2+i*4]<<8 | b[3+i*4];
	}
	return 0;
err:
	werrstr("%s: %r", "TableLoca");
	return -1;
}

void
print_TableLoca(Otfile *f, int indent, Otf *o, TableLoca *v)
{
	if(o->indexToLocFormat == 0){
		for(int i = 0; i < (o->numGlyphs+1); i++)
			f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "shortOffsets", i, v->shortOffsets[i]);
	}
	if(o->indexToLocFormat == 1){
		for(int i = 0; i < (o->numGlyphs+1); i++)
			f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "longOffsets", i, v->longOffsets[i]);
	}
	USED(o);
}

int
read_GlyphData(Otf *o, GlyphData *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 8)) == nil)
		goto err;
	v->originOffsetX = b[0]<<8 | b[1];
	v->originOffsetY = b[2]<<8 | b[3];
	v->graphicType = b[4]<<24 | b[5]<<16 | b[6]<<8 | b[7];
	return 0;
err:
	werrstr("%s: %r", "GlyphData");
	return -1;
}

void
print_GlyphData(Otfile *f, int indent, Otf *o, GlyphData *v)
{
	f->print(f->aux, "%*s%s: %d\n", indent, "", "originOffsetX", v->originOffsetX);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "originOffsetY", v->originOffsetY);
	f->print(f->aux, "%*s%s: %c%c%c%c\n", indent, "", "graphicType", v->graphicType>>24, v->graphicType>>16, v->graphicType>>8, v->graphicType>>0);
	USED(o);
}

int
read_SbixStrike(Otf *o, SbixStrike *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 4)) == nil)
		goto err;
	v->ppem = b[0]<<8 | b[1];
	v->ppi = b[2]<<8 | b[3];
	if((b = otfreadn(o, (o->numGlyphs+1)*4)) == nil)
		goto err;
	v->glyphDataOffsets = malloc((o->numGlyphs+1)*sizeof(*v->glyphDataOffsets));
	for(int i = 0; i < (o->numGlyphs+1); i++)
		v->glyphDataOffsets[i] = b[0+i*4]<<24 | b[1+i*4]<<16 | b[2+i*4]<<8 | b[3+i*4];
	if(o->numGlyphs > 0){
		v->glyphData = calloc(o->numGlyphs, sizeof(*v->glyphData));
		for(int i = 0; i < o->numGlyphs; i++){
			if(v->glyphDataOffsets[i] == 0)
				continue;
			if(otfpushrange(o, v->glyphDataOffsets[i], -1))
				goto err;
			int r = read_GlyphData(o, v->glyphData+i);
			if(otfpoprange(o) < 0)
				goto err;
			if(r < 0){
				memset(v->glyphData+i, 0, sizeof(*v->glyphData));
				break;
			}
		}
	}
	return 0;
err:
	werrstr("%s: %r", "SbixStrike");
	return -1;
}

void
print_SbixStrike(Otfile *f, int indent, Otf *o, SbixStrike *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "ppem", v->ppem);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "ppi", v->ppi);
	for(int i = 0; i < (o->numGlyphs+1); i++)
		f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "glyphDataOffsets", i, v->glyphDataOffsets[i]);
	for(int i = 0; i < o->numGlyphs; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "glyphData", i);
		print_GlyphData(f, indent+indentΔ, o, &v->glyphData[i]);
	}
	USED(o);
}

int
read_TableSbix(Otf *o, TableSbix *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 8)) == nil)
		goto err;
	u16int version = b[0]<<8 | b[1];
	if(version != 1){
		werrstr("%s: invalid value: %d (%#ux)", "version", version, version);
		goto err;
	}
	v->flags = b[2]<<8 | b[3];
	v->numStrikes = b[4]<<24 | b[5]<<16 | b[6]<<8 | b[7];
	if((b = otfreadn(o, v->numStrikes*4)) == nil)
		goto err;
	v->strikeOffsets = malloc(v->numStrikes*sizeof(*v->strikeOffsets));
	for(int i = 0; i < v->numStrikes; i++)
		v->strikeOffsets[i] = b[0+i*4]<<24 | b[1+i*4]<<16 | b[2+i*4]<<8 | b[3+i*4];
	if(v->numStrikes > 0){
		v->strikes = calloc(v->numStrikes, sizeof(*v->strikes));
		for(int i = 0; i < v->numStrikes; i++){
			if(v->strikeOffsets[i] == 0)
				continue;
			if(otfpushrange(o, v->strikeOffsets[i], -1))
				goto err;
			int r = read_SbixStrike(o, v->strikes+i);
			if(otfpoprange(o) < 0)
				goto err;
			if(r < 0){
				memset(v->strikes+i, 0, sizeof(*v->strikes));
				break;
			}
		}
	}
	return 0;
err:
	werrstr("%s: %r", "TableSbix");
	return -1;
}

void
print_TableSbix(Otfile *f, int indent, Otf *o, TableSbix *v)
{
	f->print(f->aux, "%*s%s: %#ux%s\n", indent, "", "flags", v->flags, (v->flags&SBIX_FL_DRAW_OUTLINES)?" SBIX_FL_DRAW_OUTLINES":"");
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "numStrikes", v->numStrikes);
	for(int i = 0; i < v->numStrikes; i++)
		f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "strikeOffsets", i, v->strikeOffsets[i]);
	for(int i = 0; i < v->numStrikes; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "strikes", i);
		print_SbixStrike(f, indent+indentΔ, o, &v->strikes[i]);
	}
	USED(o);
}

int
read_VariationAxisRecord(Otf *o, VariationAxisRecord *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 20)) == nil)
		goto err;
	v->axisTag = b[0]<<24 | b[1]<<16 | b[2]<<8 | b[3];
	v->minValue = (b[4]<<24 | b[5]<<16 | b[6]<<8 | b[7])/65536.0f;
	v->defaultValue = (b[8]<<24 | b[9]<<16 | b[10]<<8 | b[11])/65536.0f;
	v->maxValue = (b[12]<<24 | b[13]<<16 | b[14]<<8 | b[15])/65536.0f;
	v->flags = b[16]<<8 | b[17];
	v->axisNameID = b[18]<<8 | b[19];
	return 0;
err:
	werrstr("%s: %r", "VariationAxisRecord");
	return -1;
}

void
print_VariationAxisRecord(Otfile *f, int indent, Otf *o, VariationAxisRecord *v)
{
	f->print(f->aux, "%*s%s: %c%c%c%c\n", indent, "", "axisTag", v->axisTag>>24, v->axisTag>>16, v->axisTag>>8, v->axisTag>>0);
	f->print(f->aux, "%*s%s: %g\n", indent, "", "minValue", v->minValue);
	f->print(f->aux, "%*s%s: %g\n", indent, "", "defaultValue", v->defaultValue);
	f->print(f->aux, "%*s%s: %g\n", indent, "", "maxValue", v->maxValue);
	f->print(f->aux, "%*s%s: %#ux%s\n", indent, "", "flags", v->flags, (v->flags&VARIATIONAXISRECORD_FL_HIDDEN_AXIS)?" VARIATIONAXISRECORD_FL_HIDDEN_AXIS":"");
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "axisNameID", v->axisNameID);
	USED(o);
}

int
read_UserTuple(Otf *o, UserTuple *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, o->axisCount*4)) == nil)
		goto err;
	v->coordinates = malloc(o->axisCount*sizeof(*v->coordinates));
	for(int i = 0; i < o->axisCount; i++)
		v->coordinates[i] = (b[0+i*4]<<24 | b[1+i*4]<<16 | b[2+i*4]<<8 | b[3+i*4])/65536.0f;
	return 0;
err:
	werrstr("%s: %r", "UserTuple");
	return -1;
}

void
print_UserTuple(Otfile *f, int indent, Otf *o, UserTuple *v)
{
	for(int i = 0; i < o->axisCount; i++)
		f->print(f->aux, "%*s%s[%d]: %g\n", indent, "", "coordinates", i, v->coordinates[i]);
	USED(o);
}

int
read_InstanceRecord(Otf *o, InstanceRecord *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 4)) == nil)
		goto err;
	v->subfamilyNameID = b[0]<<8 | b[1];
	if(read_UserTuple(o, &v->coordinates) < 0){
		werrstr("%s: %r", "coordinates");
		goto err;
	}
	if((o->instanceSize==((o->axisCount*4)+6))){
		if((b = otfreadn(o, 2)) == nil)
			goto err;
		v->postScriptNameID = b[0]<<8 | b[1];
	}
	return 0;
err:
	werrstr("%s: %r", "InstanceRecord");
	return -1;
}

void
print_InstanceRecord(Otfile *f, int indent, Otf *o, InstanceRecord *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "subfamilyNameID", v->subfamilyNameID);
	f->print(f->aux, "%*s%s:\n", indent, "", "coordinates");
	print_UserTuple(f, indent+indentΔ, o, &v->coordinates);
	if((o->instanceSize==((o->axisCount*4)+6)))
		f->print(f->aux, "%*s%s: %ud\n", indent, "", "postScriptNameID", v->postScriptNameID);
	USED(o);
}

int
read_AxisInstances(Otf *o, AxisInstances *v)
{
	u8int *b = nil; USED(b);
	if(otfarray(o, &v->axes, read_VariationAxisRecord, sizeof(VariationAxisRecord), o->axisCount) < 0){
		werrstr("%s: %r", "axes");
		goto err;
	}
	if(otfarray(o, &v->instaces, read_InstanceRecord, sizeof(InstanceRecord), o->instanceCount) < 0){
		werrstr("%s: %r", "instaces");
		goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "AxisInstances");
	return -1;
}

void
print_AxisInstances(Otfile *f, int indent, Otf *o, AxisInstances *v)
{
	for(int i = 0; i < o->axisCount; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "axes", i);
		print_VariationAxisRecord(f, indent+indentΔ, o, &v->axes[i]);
	}
	for(int i = 0; i < o->instanceCount; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "instaces", i);
		print_InstanceRecord(f, indent+indentΔ, o, &v->instaces[i]);
	}
	USED(o);
}

int
read_TableFvar(Otf *o, TableFvar *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 16)) == nil)
		goto err;
	u16int majorVersion = b[0]<<8 | b[1];
	if(majorVersion != 1){
		werrstr("%s: invalid value: %d (%#ux)", "majorVersion", majorVersion, majorVersion);
		goto err;
	}
	u16int minorVersion = b[2]<<8 | b[3];
	if(minorVersion != 0){
		werrstr("%s: invalid value: %d (%#ux)", "minorVersion", minorVersion, minorVersion);
		goto err;
	}
	v->axesArrayOffset = b[4]<<8 | b[5];
	v->axisCount = b[8]<<8 | b[9];
	o->axisCount = v->axisCount;
	u16int axisSize = b[10]<<8 | b[11];
	if(axisSize != 20){
		werrstr("%s: invalid value: %d (%#ux)", "axisSize", axisSize, axisSize);
		goto err;
	}
	v->instanceCount = b[12]<<8 | b[13];
	o->instanceCount = v->instanceCount;
	v->instanceSize = b[14]<<8 | b[15];
	o->instanceSize = v->instanceSize;
	if(v->axesArrayOffset != 0){
		if(otfpushrange(o, v->axesArrayOffset, -1) < 0)
			goto err;
		v->axisInstances = calloc(1, sizeof(*v->axisInstances));
		if(read_AxisInstances(o, v->axisInstances) < 0){
			werrstr("%s: %r", "axisInstances");
			goto err;
		}
		if(otfpoprange(o) < 0)
			goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "TableFvar");
	return -1;
}

void
print_TableFvar(Otfile *f, int indent, Otf *o, TableFvar *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "axesArrayOffset", v->axesArrayOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "axisCount", v->axisCount);
	o->axisCount = v->axisCount;
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "instanceCount", v->instanceCount);
	o->instanceCount = v->instanceCount;
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "instanceSize", v->instanceSize);
	o->instanceSize = v->instanceSize;
	f->print(f->aux, "%*s%s:\n", indent, "", "axisInstances");
	if(v->axisInstances != nil)
		print_AxisInstances(f, indent+indentΔ, o, v->axisInstances);
	USED(o);
}

int
read_RegionAxisCoordinates(Otf *o, RegionAxisCoordinates *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 6)) == nil)
		goto err;
	v->startCoord = (b[0]<<8 | b[1]>>14)+(b[0]<<8 | b[1]&((1<<14)-1))/16384.0;
	v->peakCoord = (b[2]<<8 | b[3]>>14)+(b[2]<<8 | b[3]&((1<<14)-1))/16384.0;
	v->endCoord = (b[4]<<8 | b[5]>>14)+(b[4]<<8 | b[5]&((1<<14)-1))/16384.0;
	return 0;
err:
	werrstr("%s: %r", "RegionAxisCoordinates");
	return -1;
}

void
print_RegionAxisCoordinates(Otfile *f, int indent, Otf *o, RegionAxisCoordinates *v)
{
	f->print(f->aux, "%*s%s: %g\n", indent, "", "startCoord", v->startCoord);
	f->print(f->aux, "%*s%s: %g\n", indent, "", "peakCoord", v->peakCoord);
	f->print(f->aux, "%*s%s: %g\n", indent, "", "endCoord", v->endCoord);
	USED(o);
}

int
read_VariationRegion(Otf *o, VariationRegion *v)
{
	u8int *b = nil; USED(b);
	if(otfarray(o, &v->regionAxes, read_RegionAxisCoordinates, sizeof(RegionAxisCoordinates), o->axisCount) < 0){
		werrstr("%s: %r", "regionAxes");
		goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "VariationRegion");
	return -1;
}

void
print_VariationRegion(Otfile *f, int indent, Otf *o, VariationRegion *v)
{
	for(int i = 0; i < o->axisCount; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "regionAxes", i);
		print_RegionAxisCoordinates(f, indent+indentΔ, o, &v->regionAxes[i]);
	}
	USED(o);
}

int
read_VariationRegionList(Otf *o, VariationRegionList *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 4)) == nil)
		goto err;
	v->axisCount = b[0]<<8 | b[1];
	v->regionCount = b[2]<<8 | b[3];
	if(otfarray(o, &v->variationRegion, read_VariationRegion, sizeof(VariationRegion), v->regionCount) < 0){
		werrstr("%s: %r", "variationRegion");
		goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "VariationRegionList");
	return -1;
}

void
print_VariationRegionList(Otfile *f, int indent, Otf *o, VariationRegionList *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "axisCount", v->axisCount);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "regionCount", v->regionCount);
	for(int i = 0; i < v->regionCount; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "variationRegion", i);
		print_VariationRegion(f, indent+indentΔ, o, &v->variationRegion[i]);
	}
	USED(o);
}

int
read_ItemVariationData(Otf *o, ItemVariationData *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 6)) == nil)
		goto err;
	v->itemCount = b[0]<<8 | b[1];
	v->wordDeltaCount = b[2]<<8 | b[3];
	v->regionIndexCount = b[4]<<8 | b[5];
	if((b = otfreadn(o, v->regionIndexCount*2)) == nil)
		goto err;
	v->regionIndexes = malloc(v->regionIndexCount*sizeof(*v->regionIndexes));
	for(int i = 0; i < v->regionIndexCount; i++)
		v->regionIndexes[i] = b[0+i*2]<<8 | b[1+i*2];
	return 0;
err:
	werrstr("%s: %r", "ItemVariationData");
	return -1;
}

void
print_ItemVariationData(Otfile *f, int indent, Otf *o, ItemVariationData *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "itemCount", v->itemCount);
	f->print(f->aux, "%*s%s: %#ux\n", indent, "", "wordDeltaCount", v->wordDeltaCount);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "regionIndexCount", v->regionIndexCount);
	for(int i = 0; i < v->regionIndexCount; i++)
		f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "regionIndexes", i, v->regionIndexes[i]);
	USED(o);
}

int
read_ItemVariationStore(Otf *o, ItemVariationStore *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 8)) == nil)
		goto err;
	u16int format = b[0]<<8 | b[1];
	if(format != 1){
		werrstr("%s: invalid value: %d (%#ux)", "format", format, format);
		goto err;
	}
	v->variationRegionListOffset = b[2]<<24 | b[3]<<16 | b[4]<<8 | b[5];
	v->itemVariationDataCount = b[6]<<8 | b[7];
	if((b = otfreadn(o, v->itemVariationDataCount*4)) == nil)
		goto err;
	v->itemVariationDataOffsets = malloc(v->itemVariationDataCount*sizeof(*v->itemVariationDataOffsets));
	for(int i = 0; i < v->itemVariationDataCount; i++)
		v->itemVariationDataOffsets[i] = b[0+i*4]<<24 | b[1+i*4]<<16 | b[2+i*4]<<8 | b[3+i*4];
	if(v->itemVariationDataCount > 0){
		v->itemVariationData = calloc(v->itemVariationDataCount, sizeof(*v->itemVariationData));
		for(int i = 0; i < v->itemVariationDataCount; i++){
			if(v->itemVariationDataOffsets[i] == 0)
				continue;
			if(otfpushrange(o, v->itemVariationDataOffsets[i], -1))
				goto err;
			int r = read_ItemVariationData(o, v->itemVariationData+i);
			if(otfpoprange(o) < 0)
				goto err;
			if(r < 0){
				memset(v->itemVariationData+i, 0, sizeof(*v->itemVariationData));
				break;
			}
		}
	}
	return 0;
err:
	werrstr("%s: %r", "ItemVariationStore");
	return -1;
}

void
print_ItemVariationStore(Otfile *f, int indent, Otf *o, ItemVariationStore *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "variationRegionListOffset", v->variationRegionListOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "itemVariationDataCount", v->itemVariationDataCount);
	for(int i = 0; i < v->itemVariationDataCount; i++)
		f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "itemVariationDataOffsets", i, v->itemVariationDataOffsets[i]);
	for(int i = 0; i < v->itemVariationDataCount; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "itemVariationData", i);
		print_ItemVariationData(f, indent+indentΔ, o, &v->itemVariationData[i]);
	}
	USED(o);
}

int
read_DeltaSetIndexMap(Otf *o, DeltaSetIndexMap *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 2)) == nil)
		goto err;
	v->format = b[0];
	if(v->format != 0 && v->format != 1){
		werrstr("%s: invalid value: %d (%#ux)", "format", v->format, v->format);
		goto err;
	}
	v->entryFormat = b[1];
	if(v->format == 0){
		if((b = otfreadn(o, 2)) == nil)
			goto err;
		v->mapCount0 = b[0]<<8 | b[1];
	}
	if(v->format == 1){
		if((b = otfreadn(o, 4)) == nil)
			goto err;
		v->mapCount1 = b[0]<<24 | b[1]<<16 | b[2]<<8 | b[3];
	}
	if((b = otfreadn(o, 1)) == nil)
		goto err;
	v->mapData = b[0];
	return 0;
err:
	werrstr("%s: %r", "DeltaSetIndexMap");
	return -1;
}

void
print_DeltaSetIndexMap(Otfile *f, int indent, Otf *o, DeltaSetIndexMap *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "format", v->format);
	f->print(f->aux, "%*s%s: %#ux\n", indent, "", "entryFormat", v->entryFormat);
	if(v->format == 0)
		f->print(f->aux, "%*s%s: %ud\n", indent, "", "mapCount0", v->mapCount0);
	if(v->format == 1)
		f->print(f->aux, "%*s%s: %ud\n", indent, "", "mapCount1", v->mapCount1);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "mapData", v->mapData);
	USED(o);
}

int
read_TableHVAR(Otf *o, TableHVAR *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 20)) == nil)
		goto err;
	u16int majorVersion = b[0]<<8 | b[1];
	if(majorVersion != 1){
		werrstr("%s: invalid value: %d (%#ux)", "majorVersion", majorVersion, majorVersion);
		goto err;
	}
	u16int minorVersion = b[2]<<8 | b[3];
	if(minorVersion != 0){
		werrstr("%s: invalid value: %d (%#ux)", "minorVersion", minorVersion, minorVersion);
		goto err;
	}
	v->itemVariationStoreOffset = b[4]<<24 | b[5]<<16 | b[6]<<8 | b[7];
	v->advanceWidthMappingOffset = b[8]<<24 | b[9]<<16 | b[10]<<8 | b[11];
	v->lsbMappingOffset = b[12]<<24 | b[13]<<16 | b[14]<<8 | b[15];
	v->rsbMappingOffset = b[16]<<24 | b[17]<<16 | b[18]<<8 | b[19];
	if(v->itemVariationStoreOffset != 0){
		if(otfpushrange(o, v->itemVariationStoreOffset, -1) < 0)
			goto err;
		v->itemVariationStore = calloc(1, sizeof(*v->itemVariationStore));
		if(read_ItemVariationStore(o, v->itemVariationStore) < 0){
			werrstr("%s: %r", "itemVariationStore");
			goto err;
		}
		if(otfpoprange(o) < 0)
			goto err;
	}
	if(v->advanceWidthMappingOffset != 0){
		if(otfpushrange(o, v->advanceWidthMappingOffset, -1) < 0)
			goto err;
		v->advanceWidthMapping = calloc(1, sizeof(*v->advanceWidthMapping));
		if(read_DeltaSetIndexMap(o, v->advanceWidthMapping) < 0){
			werrstr("%s: %r", "advanceWidthMapping");
			goto err;
		}
		if(otfpoprange(o) < 0)
			goto err;
	}
	if(v->lsbMappingOffset != 0){
		if(otfpushrange(o, v->lsbMappingOffset, -1) < 0)
			goto err;
		v->lsbMapping = calloc(1, sizeof(*v->lsbMapping));
		if(read_DeltaSetIndexMap(o, v->lsbMapping) < 0){
			werrstr("%s: %r", "lsbMapping");
			goto err;
		}
		if(otfpoprange(o) < 0)
			goto err;
	}
	if(v->rsbMappingOffset != 0){
		if(otfpushrange(o, v->rsbMappingOffset, -1) < 0)
			goto err;
		v->rsbMapping = calloc(1, sizeof(*v->rsbMapping));
		if(read_DeltaSetIndexMap(o, v->rsbMapping) < 0){
			werrstr("%s: %r", "rsbMapping");
			goto err;
		}
		if(otfpoprange(o) < 0)
			goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "TableHVAR");
	return -1;
}

void
print_TableHVAR(Otfile *f, int indent, Otf *o, TableHVAR *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "itemVariationStoreOffset", v->itemVariationStoreOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "advanceWidthMappingOffset", v->advanceWidthMappingOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "lsbMappingOffset", v->lsbMappingOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "rsbMappingOffset", v->rsbMappingOffset);
	f->print(f->aux, "%*s%s:\n", indent, "", "itemVariationStore");
	if(v->itemVariationStore != nil)
		print_ItemVariationStore(f, indent+indentΔ, o, v->itemVariationStore);
	f->print(f->aux, "%*s%s:\n", indent, "", "advanceWidthMapping");
	if(v->advanceWidthMapping != nil)
		print_DeltaSetIndexMap(f, indent+indentΔ, o, v->advanceWidthMapping);
	f->print(f->aux, "%*s%s:\n", indent, "", "lsbMapping");
	if(v->lsbMapping != nil)
		print_DeltaSetIndexMap(f, indent+indentΔ, o, v->lsbMapping);
	f->print(f->aux, "%*s%s:\n", indent, "", "rsbMapping");
	if(v->rsbMapping != nil)
		print_DeltaSetIndexMap(f, indent+indentΔ, o, v->rsbMapping);
	USED(o);
}

int
read_TableFFTM(Otf *o, TableFFTM *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 4)) == nil)
		goto err;
	v->version = b[0]<<24 | b[1]<<16 | b[2]<<8 | b[3];
	if(v->version == 1){
		if((b = otfreadn(o, 24)) == nil)
			goto err;
		v->fontforge = ((s64int)b[0]<<56 | (s64int)b[1]<<48 | (s64int)b[2]<<40 | (s64int)b[3]<<32 | b[4]<<24 | b[5]<<16 | b[6]<<8 | b[7]) - 2082844800LL;
		v->created = ((s64int)b[8]<<56 | (s64int)b[9]<<48 | (s64int)b[10]<<40 | (s64int)b[11]<<32 | b[12]<<24 | b[13]<<16 | b[14]<<8 | b[15]) - 2082844800LL;
		v->modified = ((s64int)b[16]<<56 | (s64int)b[17]<<48 | (s64int)b[18]<<40 | (s64int)b[19]<<32 | b[20]<<24 | b[21]<<16 | b[22]<<8 | b[23]) - 2082844800LL;
	}
	return 0;
err:
	werrstr("%s: %r", "TableFFTM");
	return -1;
}

void
print_TableFFTM(Otfile *f, int indent, Otf *o, TableFFTM *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "version", v->version);
	if(v->version == 1)
		f->print(f->aux, "%*s%s: %τ\n", indent, "", "fontforge", fmttime(v->fontforge));
	if(v->version == 1)
		f->print(f->aux, "%*s%s: %τ\n", indent, "", "created", fmttime(v->created));
	if(v->version == 1)
		f->print(f->aux, "%*s%s: %τ\n", indent, "", "modified", fmttime(v->modified));
	USED(o);
}

int
read_SignatureBlock1(Otf *o, SignatureBlock1 *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 8)) == nil)
		goto err;
	v->signatureLength = b[4]<<24 | b[5]<<16 | b[6]<<8 | b[7];
	if((b = otfreadn(o, v->signatureLength*1)) == nil)
		goto err;
	v->signature = malloc(v->signatureLength*sizeof(*v->signature));
	for(int i = 0; i < v->signatureLength; i++)
		v->signature[i] = b[0+i*1];
	return 0;
err:
	werrstr("%s: %r", "SignatureBlock1");
	return -1;
}

void
print_SignatureBlock1(Otfile *f, int indent, Otf *o, SignatureBlock1 *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "signatureLength", v->signatureLength);
	for(int i = 0; i < v->signatureLength; i++)
		f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "signature", i, v->signature[i]);
	USED(o);
}

int
read_SignatureRecord(Otf *o, SignatureRecord *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 12)) == nil)
		goto err;
	v->format = b[0]<<24 | b[1]<<16 | b[2]<<8 | b[3];
	v->length = b[4]<<24 | b[5]<<16 | b[6]<<8 | b[7];
	v->signatureBlockOffset = b[8]<<24 | b[9]<<16 | b[10]<<8 | b[11];
	if(v->format == 1){
		if(v->signatureBlockOffset != 0){
			if(otfpushrange(o, v->signatureBlockOffset, -1) < 0)
				goto err;
			v->signatureBlock1 = calloc(1, sizeof(*v->signatureBlock1));
			if(read_SignatureBlock1(o, v->signatureBlock1) < 0){
				werrstr("%s: %r", "signatureBlock1");
				goto err;
			}
			if(otfpoprange(o) < 0)
				goto err;
		}
	}
	return 0;
err:
	werrstr("%s: %r", "SignatureRecord");
	return -1;
}

void
print_SignatureRecord(Otfile *f, int indent, Otf *o, SignatureRecord *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "format", v->format);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "length", v->length);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "signatureBlockOffset", v->signatureBlockOffset);
	if(v->format == 1){
		f->print(f->aux, "%*s%s:\n", indent, "", "signatureBlock1");
		if(v->signatureBlock1 != nil)
			print_SignatureBlock1(f, indent+indentΔ, o, v->signatureBlock1);
	}
	USED(o);
}

int
read_TableDSIG(Otf *o, TableDSIG *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 8)) == nil)
		goto err;
	u32int version = b[0]<<24 | b[1]<<16 | b[2]<<8 | b[3];
	if(version != 1){
		werrstr("%s: invalid value: %d (%#ux)", "version", version, version);
		goto err;
	}
	v->numSignatures = b[4]<<8 | b[5];
	v->flags = b[6]<<8 | b[7];
	if(otfarray(o, &v->signatureRecords, read_SignatureRecord, sizeof(SignatureRecord), v->numSignatures) < 0){
		werrstr("%s: %r", "signatureRecords");
		goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "TableDSIG");
	return -1;
}

void
print_TableDSIG(Otfile *f, int indent, Otf *o, TableDSIG *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "numSignatures", v->numSignatures);
	f->print(f->aux, "%*s%s: %#ux%s\n", indent, "", "flags", v->flags, (v->flags&DSIG_CANNOT_BE_RESIGNED)?" DSIG_CANNOT_BE_RESIGNED":"");
	for(int i = 0; i < v->numSignatures; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "signatureRecords", i);
		print_SignatureRecord(f, indent+indentΔ, o, &v->signatureRecords[i]);
	}
	USED(o);
}

int
read_AxisRecord(Otf *o, AxisRecord *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 8)) == nil)
		goto err;
	v->axisTag = b[0]<<24 | b[1]<<16 | b[2]<<8 | b[3];
	v->axisNameID = b[4]<<8 | b[5];
	v->axisOrdering = b[6]<<8 | b[7];
	if((b = otfreadn(o, (o->designAxisSize-8)*1)) == nil)
		goto err;
	USED(b);
	return 0;
err:
	werrstr("%s: %r", "AxisRecord");
	return -1;
}

void
print_AxisRecord(Otfile *f, int indent, Otf *o, AxisRecord *v)
{
	f->print(f->aux, "%*s%s: %c%c%c%c\n", indent, "", "axisTag", v->axisTag>>24, v->axisTag>>16, v->axisTag>>8, v->axisTag>>0);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "axisNameID", v->axisNameID);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "axisOrdering", v->axisOrdering);
	USED(o);
}

int
read_AxisValueTable1(Otf *o, AxisValueTable1 *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 10)) == nil)
		goto err;
	v->axisIndex = b[0]<<8 | b[1];
	v->flags = b[2]<<8 | b[3];
	v->valueNameID = b[4]<<8 | b[5];
	v->value = (b[6]<<24 | b[7]<<16 | b[8]<<8 | b[9])/65536.0f;
	return 0;
err:
	werrstr("%s: %r", "AxisValueTable1");
	return -1;
}

void
print_AxisValueTable1(Otfile *f, int indent, Otf *o, AxisValueTable1 *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "axisIndex", v->axisIndex);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "flags", v->flags);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "valueNameID", v->valueNameID);
	f->print(f->aux, "%*s%s: %g\n", indent, "", "value", v->value);
	USED(o);
}

int
read_AxisValueTable2(Otf *o, AxisValueTable2 *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 18)) == nil)
		goto err;
	v->axisIndex = b[0]<<8 | b[1];
	v->flags = b[2]<<8 | b[3];
	v->valueNameID = b[4]<<8 | b[5];
	v->nominalValue = (b[6]<<24 | b[7]<<16 | b[8]<<8 | b[9])/65536.0f;
	v->rangeMinValue = (b[10]<<24 | b[11]<<16 | b[12]<<8 | b[13])/65536.0f;
	v->rangeMaxValue = (b[14]<<24 | b[15]<<16 | b[16]<<8 | b[17])/65536.0f;
	return 0;
err:
	werrstr("%s: %r", "AxisValueTable2");
	return -1;
}

void
print_AxisValueTable2(Otfile *f, int indent, Otf *o, AxisValueTable2 *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "axisIndex", v->axisIndex);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "flags", v->flags);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "valueNameID", v->valueNameID);
	f->print(f->aux, "%*s%s: %g\n", indent, "", "nominalValue", v->nominalValue);
	f->print(f->aux, "%*s%s: %g\n", indent, "", "rangeMinValue", v->rangeMinValue);
	f->print(f->aux, "%*s%s: %g\n", indent, "", "rangeMaxValue", v->rangeMaxValue);
	USED(o);
}

int
read_AxisValueTable3(Otf *o, AxisValueTable3 *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 14)) == nil)
		goto err;
	v->axisIndex = b[0]<<8 | b[1];
	v->flags = b[2]<<8 | b[3];
	v->valueNameID = b[4]<<8 | b[5];
	v->value = (b[6]<<24 | b[7]<<16 | b[8]<<8 | b[9])/65536.0f;
	v->linkedValue = (b[10]<<24 | b[11]<<16 | b[12]<<8 | b[13])/65536.0f;
	return 0;
err:
	werrstr("%s: %r", "AxisValueTable3");
	return -1;
}

void
print_AxisValueTable3(Otfile *f, int indent, Otf *o, AxisValueTable3 *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "axisIndex", v->axisIndex);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "flags", v->flags);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "valueNameID", v->valueNameID);
	f->print(f->aux, "%*s%s: %g\n", indent, "", "value", v->value);
	f->print(f->aux, "%*s%s: %g\n", indent, "", "linkedValue", v->linkedValue);
	USED(o);
}

int
read_AxisValue(Otf *o, AxisValue *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 6)) == nil)
		goto err;
	v->axisIndex = b[0]<<8 | b[1];
	v->value = (b[2]<<24 | b[3]<<16 | b[4]<<8 | b[5])/65536.0f;
	return 0;
err:
	werrstr("%s: %r", "AxisValue");
	return -1;
}

void
print_AxisValue(Otfile *f, int indent, Otf *o, AxisValue *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "axisIndex", v->axisIndex);
	f->print(f->aux, "%*s%s: %g\n", indent, "", "value", v->value);
	USED(o);
}

int
read_AxisValueTable4(Otf *o, AxisValueTable4 *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 6)) == nil)
		goto err;
	v->axisCount = b[0]<<8 | b[1];
	v->flags = b[2]<<8 | b[3];
	v->valueNameID = b[4]<<8 | b[5];
	if(otfarray(o, &v->axisValues, read_AxisValue, sizeof(AxisValue), v->axisCount) < 0){
		werrstr("%s: %r", "axisValues");
		goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "AxisValueTable4");
	return -1;
}

void
print_AxisValueTable4(Otfile *f, int indent, Otf *o, AxisValueTable4 *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "axisCount", v->axisCount);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "flags", v->flags);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "valueNameID", v->valueNameID);
	for(int i = 0; i < v->axisCount; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "axisValues", i);
		print_AxisValue(f, indent+indentΔ, o, &v->axisValues[i]);
	}
	USED(o);
}

int
read_AxisValueTable(Otf *o, AxisValueTable *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 2)) == nil)
		goto err;
	v->format = b[0]<<8 | b[1];
	if(v->format == 1){
		if(read_AxisValueTable1(o, &v->f1) < 0){
			werrstr("%s: %r", "f1");
			goto err;
		}
	}
	if(v->format == 2){
		if(read_AxisValueTable2(o, &v->f2) < 0){
			werrstr("%s: %r", "f2");
			goto err;
		}
	}
	if(v->format == 3){
		if(read_AxisValueTable3(o, &v->f3) < 0){
			werrstr("%s: %r", "f3");
			goto err;
		}
	}
	if(v->format == 4){
		if(read_AxisValueTable3(o, &v->f4) < 0){
			werrstr("%s: %r", "f4");
			goto err;
		}
	}
	return 0;
err:
	werrstr("%s: %r", "AxisValueTable");
	return -1;
}

void
print_AxisValueTable(Otfile *f, int indent, Otf *o, AxisValueTable *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "format", v->format);
	if(v->format == 1){
		f->print(f->aux, "%*s%s:\n", indent, "", "f1");
		print_AxisValueTable1(f, indent+indentΔ, o, &v->f1);
	}
	if(v->format == 2){
		f->print(f->aux, "%*s%s:\n", indent, "", "f2");
		print_AxisValueTable2(f, indent+indentΔ, o, &v->f2);
	}
	if(v->format == 3){
		f->print(f->aux, "%*s%s:\n", indent, "", "f3");
		print_AxisValueTable3(f, indent+indentΔ, o, &v->f3);
	}
	if(v->format == 4){
		f->print(f->aux, "%*s%s:\n", indent, "", "f4");
		print_AxisValueTable3(f, indent+indentΔ, o, &v->f4);
	}
	USED(o);
}

int
read_DesignAxes(Otf *o, DesignAxes *v)
{
	u8int *b = nil; USED(b);
	if(otfarray(o, &v->designAxes, read_AxisRecord, sizeof(AxisRecord), o->designAxisCount) < 0){
		werrstr("%s: %r", "designAxes");
		goto err;
	}
	if((b = otfreadn(o, o->axisValueCount*2)) == nil)
		goto err;
	v->axisValueTableOffsets = malloc(o->axisValueCount*sizeof(*v->axisValueTableOffsets));
	for(int i = 0; i < o->axisValueCount; i++)
		v->axisValueTableOffsets[i] = b[0+i*2]<<8 | b[1+i*2];
	if(o->axisValueCount > 0){
		v->axisValueTables = calloc(o->axisValueCount, sizeof(*v->axisValueTables));
		for(int i = 0; i < o->axisValueCount; i++){
			if(v->axisValueTableOffsets[i] == 0)
				continue;
			if(otfpushrange(o, v->axisValueTableOffsets[i], -1))
				goto err;
			int r = read_AxisValueTable(o, v->axisValueTables+i);
			if(otfpoprange(o) < 0)
				goto err;
			if(r < 0){
				memset(v->axisValueTables+i, 0, sizeof(*v->axisValueTables));
				break;
			}
		}
	}
	return 0;
err:
	werrstr("%s: %r", "DesignAxes");
	return -1;
}

void
print_DesignAxes(Otfile *f, int indent, Otf *o, DesignAxes *v)
{
	for(int i = 0; i < o->designAxisCount; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "designAxes", i);
		print_AxisRecord(f, indent+indentΔ, o, &v->designAxes[i]);
	}
	for(int i = 0; i < o->axisValueCount; i++)
		f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "axisValueTableOffsets", i, v->axisValueTableOffsets[i]);
	for(int i = 0; i < o->axisValueCount; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "axisValueTables", i);
		print_AxisValueTable(f, indent+indentΔ, o, &v->axisValueTables[i]);
	}
	USED(o);
}

int
read_TableSTAT(Otf *o, TableSTAT *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 18)) == nil)
		goto err;
	u16int majorVersion = b[0]<<8 | b[1];
	if(majorVersion != 1){
		werrstr("%s: invalid value: %d (%#ux)", "majorVersion", majorVersion, majorVersion);
		goto err;
	}
	v->minorVersion = b[2]<<8 | b[3];
	if(v->minorVersion != 0 && v->minorVersion != 1 && v->minorVersion != 2){
		werrstr("%s: invalid value: %d (%#ux)", "minorVersion", v->minorVersion, v->minorVersion);
		goto err;
	}
	v->designAxisSize = b[4]<<8 | b[5];
	o->designAxisSize = v->designAxisSize;
	v->designAxisCount = b[6]<<8 | b[7];
	o->designAxisCount = v->designAxisCount;
	v->designAxesOffset = b[8]<<24 | b[9]<<16 | b[10]<<8 | b[11];
	v->axisValueCount = b[12]<<8 | b[13];
	o->axisValueCount = v->axisValueCount;
	v->offsetToAxisValueOffsets = b[14]<<24 | b[15]<<16 | b[16]<<8 | b[17];
	if(v->minorVersion > 0){
		if((b = otfreadn(o, 2)) == nil)
			goto err;
		v->elidedFallbackNameID = b[0]<<8 | b[1];
	}
	if(v->designAxesOffset != 0){
		if(otfpushrange(o, v->designAxesOffset, -1) < 0)
			goto err;
		v->designAxes = calloc(1, sizeof(*v->designAxes));
		if(read_DesignAxes(o, v->designAxes) < 0){
			werrstr("%s: %r", "designAxes");
			goto err;
		}
		if(otfpoprange(o) < 0)
			goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "TableSTAT");
	return -1;
}

void
print_TableSTAT(Otfile *f, int indent, Otf *o, TableSTAT *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "minorVersion", v->minorVersion);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "designAxisSize", v->designAxisSize);
	o->designAxisSize = v->designAxisSize;
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "designAxisCount", v->designAxisCount);
	o->designAxisCount = v->designAxisCount;
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "designAxesOffset", v->designAxesOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "axisValueCount", v->axisValueCount);
	o->axisValueCount = v->axisValueCount;
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "offsetToAxisValueOffsets", v->offsetToAxisValueOffsets);
	if(v->minorVersion > 0)
		f->print(f->aux, "%*s%s: %ud\n", indent, "", "elidedFallbackNameID", v->elidedFallbackNameID);
	f->print(f->aux, "%*s%s:\n", indent, "", "designAxes");
	if(v->designAxes != nil)
		print_DesignAxes(f, indent+indentΔ, o, v->designAxes);
	USED(o);
}

int
read_GaspRange(Otf *o, GaspRange *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 4)) == nil)
		goto err;
	v->rangeMaxPPEM = b[0]<<8 | b[1];
	v->rangeGaspBehavior = b[2]<<8 | b[3];
	return 0;
err:
	werrstr("%s: %r", "GaspRange");
	return -1;
}

void
print_GaspRange(Otfile *f, int indent, Otf *o, GaspRange *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "rangeMaxPPEM", v->rangeMaxPPEM);
	f->print(f->aux, "%*s%s: %#ux%s%s%s%s\n", indent, "", "rangeGaspBehavior", v->rangeGaspBehavior, (v->rangeGaspBehavior&GASP_GRIDFIT)?" GASP_GRIDFIT":"", (v->rangeGaspBehavior&GASP_DOGRAY)?" GASP_DOGRAY":"", (v->rangeGaspBehavior&GASP_SYMMETRIC_GRIDFIT)?" GASP_SYMMETRIC_GRIDFIT":"", (v->rangeGaspBehavior&GASP_SYMMETRIC_SMOOTHING)?" GASP_SYMMETRIC_SMOOTHING":"");
	USED(o);
}

int
read_TableGasp(Otf *o, TableGasp *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 4)) == nil)
		goto err;
	v->version = b[0]<<8 | b[1];
	if(v->version != 0 && v->version != 1){
		werrstr("%s: invalid value: %d (%#ux)", "version", v->version, v->version);
		goto err;
	}
	v->numRanges = b[2]<<8 | b[3];
	if(otfarray(o, &v->gaspRanges, read_GaspRange, sizeof(GaspRange), v->numRanges) < 0){
		werrstr("%s: %r", "gaspRanges");
		goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "TableGasp");
	return -1;
}

void
print_TableGasp(Otfile *f, int indent, Otf *o, TableGasp *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "version", v->version);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "numRanges", v->numRanges);
	for(int i = 0; i < v->numRanges; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "gaspRanges", i);
		print_GaspRange(f, indent+indentΔ, o, &v->gaspRanges[i]);
	}
	USED(o);
}

int
read_Tuple(Otf *o, Tuple *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, o->axisCount*2)) == nil)
		goto err;
	v->coordinates = malloc(o->axisCount*sizeof(*v->coordinates));
	for(int i = 0; i < o->axisCount; i++)
		v->coordinates[i] = (b[0+i*2]<<8 | b[1+i*2]>>14)+(b[0+i*2]<<8 | b[1+i*2]&((1<<14)-1))/16384.0;
	return 0;
err:
	werrstr("%s: %r", "Tuple");
	return -1;
}

void
print_Tuple(Otfile *f, int indent, Otf *o, Tuple *v)
{
	for(int i = 0; i < o->axisCount; i++)
		f->print(f->aux, "%*s%s[%d]: %g\n", indent, "", "coordinates", i, v->coordinates[i]);
	USED(o);
}

int
read_TupleVariationHeader(Otf *o, TupleVariationHeader *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 4)) == nil)
		goto err;
	v->variationDataSize = b[0]<<8 | b[1];
	v->tupleIndex = b[2]<<8 | b[3];
	if((v->tupleIndex&TUPLEINDEX_FL_EMBEDDED_PEAK_TUPLE) != 0){
		if(read_Tuple(o, &v->peakTuple) < 0){
			werrstr("%s: %r", "peakTuple");
			goto err;
		}
	}
	if((v->tupleIndex&TUPLEINDEX_FL_INTERMEDIATE_REGION) != 0){
		if(read_Tuple(o, &v->intermediateStartTuple) < 0){
			werrstr("%s: %r", "intermediateStartTuple");
			goto err;
		}
	}
	if((v->tupleIndex&TUPLEINDEX_FL_INTERMEDIATE_REGION) != 0){
		if(read_Tuple(o, &v->intermediateEndTuple) < 0){
			werrstr("%s: %r", "intermediateEndTuple");
			goto err;
		}
	}
	return 0;
err:
	werrstr("%s: %r", "TupleVariationHeader");
	return -1;
}

void
print_TupleVariationHeader(Otfile *f, int indent, Otf *o, TupleVariationHeader *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "variationDataSize", v->variationDataSize);
	f->print(f->aux, "%*s%s: %ud%s%s%s\n", indent, "", "tupleIndex", v->tupleIndex, (v->tupleIndex&TUPLEINDEX_FL_PRIVATE_POINT_NUMBERS)?" TUPLEINDEX_FL_PRIVATE_POINT_NUMBERS":"", (v->tupleIndex&TUPLEINDEX_FL_INTERMEDIATE_REGION)?" TUPLEINDEX_FL_INTERMEDIATE_REGION":"", (v->tupleIndex&TUPLEINDEX_FL_EMBEDDED_PEAK_TUPLE)?" TUPLEINDEX_FL_EMBEDDED_PEAK_TUPLE":"");
	if((v->tupleIndex&TUPLEINDEX_FL_EMBEDDED_PEAK_TUPLE) != 0){
		f->print(f->aux, "%*s%s:\n", indent, "", "peakTuple");
		print_Tuple(f, indent+indentΔ, o, &v->peakTuple);
	}
	if((v->tupleIndex&TUPLEINDEX_FL_INTERMEDIATE_REGION) != 0){
		f->print(f->aux, "%*s%s:\n", indent, "", "intermediateStartTuple");
		print_Tuple(f, indent+indentΔ, o, &v->intermediateStartTuple);
	}
	if((v->tupleIndex&TUPLEINDEX_FL_INTERMEDIATE_REGION) != 0){
		f->print(f->aux, "%*s%s:\n", indent, "", "intermediateEndTuple");
		print_Tuple(f, indent+indentΔ, o, &v->intermediateEndTuple);
	}
	USED(o);
}

int
read_GlyphVariationData(Otf *o, GlyphVariationData *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 4)) == nil)
		goto err;
	v->tupleVariationCount = b[0]<<8 | b[1];
	v->dataOffset = b[2]<<8 | b[3];
	if(otfarray(o, &v->tupleVariationHeaders, read_TupleVariationHeader, sizeof(TupleVariationHeader), (v->tupleVariationCount&4095)) < 0){
		werrstr("%s: %r", "tupleVariationHeaders");
		goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "GlyphVariationData");
	return -1;
}

void
print_GlyphVariationData(Otfile *f, int indent, Otf *o, GlyphVariationData *v)
{
	f->print(f->aux, "%*s%s: %ud%s\n", indent, "", "tupleVariationCount", v->tupleVariationCount, (v->tupleVariationCount&COUNT_FL_SHARED_POINT_NUMBERS)?" COUNT_FL_SHARED_POINT_NUMBERS":"");
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "dataOffset", v->dataOffset);
	for(int i = 0; i < (v->tupleVariationCount&4095); i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "tupleVariationHeaders", i);
		print_TupleVariationHeader(f, indent+indentΔ, o, &v->tupleVariationHeaders[i]);
	}
	USED(o);
}

int
read_TableGvar(Otf *o, TableGvar *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 20)) == nil)
		goto err;
	u16int majorVersion = b[0]<<8 | b[1];
	if(majorVersion != 1){
		werrstr("%s: invalid value: %d (%#ux)", "majorVersion", majorVersion, majorVersion);
		goto err;
	}
	u16int minorVersion = b[2]<<8 | b[3];
	if(minorVersion != 0){
		werrstr("%s: invalid value: %d (%#ux)", "minorVersion", minorVersion, minorVersion);
		goto err;
	}
	v->axisCount = b[4]<<8 | b[5];
	o->axisCount = v->axisCount;
	v->sharedTupleCount = b[6]<<8 | b[7];
	v->sharedTuplesOffset = b[8]<<24 | b[9]<<16 | b[10]<<8 | b[11];
	v->glyphCount = b[12]<<8 | b[13];
	v->flags = b[14]<<8 | b[15];
	v->glyphVariationDataArrayOffset = b[16]<<24 | b[17]<<16 | b[18]<<8 | b[19];
	if((v->flags&GVAR_FL_LONG_OFFSETS) == 0){
		if((b = otfreadn(o, (v->glyphCount+1)*2)) == nil)
			goto err;
		v->glyphVariationDataOffsetsShort = malloc((v->glyphCount+1)*sizeof(*v->glyphVariationDataOffsetsShort));
		for(int i = 0; i < (v->glyphCount+1); i++)
			v->glyphVariationDataOffsetsShort[i] = b[0+i*2]<<8 | b[1+i*2];
	}
	if((v->flags&GVAR_FL_LONG_OFFSETS) != 0){
		if((b = otfreadn(o, (v->glyphCount+1)*4)) == nil)
			goto err;
		v->glyphVariationDataOffsetsLong = malloc((v->glyphCount+1)*sizeof(*v->glyphVariationDataOffsetsLong));
		for(int i = 0; i < (v->glyphCount+1); i++)
			v->glyphVariationDataOffsetsLong[i] = b[0+i*4]<<24 | b[1+i*4]<<16 | b[2+i*4]<<8 | b[3+i*4];
	}
	if((v->flags&GVAR_FL_LONG_OFFSETS) == 0){
		if(v->glyphVariationDataArrayOffset != 0 && v->glyphCount > 0){
			v->glyphVariationData = calloc(v->glyphCount, sizeof(*v->glyphVariationData));
			for(int i = 0; i < v->glyphCount; i++){
				if(otfpushrange(o, v->glyphVariationDataArrayOffset+v->glyphVariationDataOffsetsShort[i], -1))
					goto err;
				int r = read_GlyphVariationData(o, v->glyphVariationData+i);
				if(otfpoprange(o) < 0)
					goto err;
				if(r < 0){
					memset(v->glyphVariationData+i, 0, sizeof(*v->glyphVariationData));
					break;
				}
			}
		}
	}
	if((v->flags&GVAR_FL_LONG_OFFSETS) != 0){
		if(v->glyphVariationDataArrayOffset != 0 && v->glyphCount > 0){
			v->glyphVariationData = calloc(v->glyphCount, sizeof(*v->glyphVariationData));
			for(int i = 0; i < v->glyphCount; i++){
				if(otfpushrange(o, v->glyphVariationDataArrayOffset+v->glyphVariationDataOffsetsLong[i], -1))
					goto err;
				int r = read_GlyphVariationData(o, v->glyphVariationData+i);
				if(otfpoprange(o) < 0)
					goto err;
				if(r < 0){
					memset(v->glyphVariationData+i, 0, sizeof(*v->glyphVariationData));
					break;
				}
			}
		}
	}
	if(v->sharedTuplesOffset != 0){
		if(otfpushrange(o, v->sharedTuplesOffset, -1) < 0)
			goto err;
		if(otfarray(o, &v->sharedTuples, read_Tuple, sizeof(Tuple), v->sharedTupleCount) < 0){
			werrstr("%s: %r", "sharedTuples");
			goto err;
		}
		if(otfpoprange(o) < 0)
			goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "TableGvar");
	return -1;
}

void
print_TableGvar(Otfile *f, int indent, Otf *o, TableGvar *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "axisCount", v->axisCount);
	o->axisCount = v->axisCount;
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "sharedTupleCount", v->sharedTupleCount);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "sharedTuplesOffset", v->sharedTuplesOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "glyphCount", v->glyphCount);
	f->print(f->aux, "%*s%s: %#ux%s\n", indent, "", "flags", v->flags, (v->flags&GVAR_FL_LONG_OFFSETS)?" GVAR_FL_LONG_OFFSETS":"");
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "glyphVariationDataArrayOffset", v->glyphVariationDataArrayOffset);
	if((v->flags&GVAR_FL_LONG_OFFSETS) == 0){
		for(int i = 0; i < (v->glyphCount+1); i++)
			f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "glyphVariationDataOffsetsShort", i, v->glyphVariationDataOffsetsShort[i]);
	}
	if((v->flags&GVAR_FL_LONG_OFFSETS) != 0){
		for(int i = 0; i < (v->glyphCount+1); i++)
			f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "glyphVariationDataOffsetsLong", i, v->glyphVariationDataOffsetsLong[i]);
	}
	if((v->flags&GVAR_FL_LONG_OFFSETS) == 0){
		for(int i = 0; i < v->glyphCount; i++){
			f->print(f->aux, "%*s%s[%d]:\n", indent, "", "glyphVariationData", i);
			print_GlyphVariationData(f, indent+indentΔ, o, &v->glyphVariationData[i]);
		}
	}
	if((v->flags&GVAR_FL_LONG_OFFSETS) != 0){
		for(int i = 0; i < v->glyphCount; i++){
			f->print(f->aux, "%*s%s[%d]:\n", indent, "", "glyphVariationData", i);
			print_GlyphVariationData(f, indent+indentΔ, o, &v->glyphVariationData[i]);
		}
	}
	for(int i = 0; i < v->sharedTupleCount; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "sharedTuples", i);
		print_Tuple(f, indent+indentΔ, o, &v->sharedTuples[i]);
	}
	USED(o);
}

int
read_AxisValueMap(Otf *o, AxisValueMap *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 4)) == nil)
		goto err;
	v->fromCoordinate = (b[0]<<8 | b[1]>>14)+(b[0]<<8 | b[1]&((1<<14)-1))/16384.0;
	v->toCoordinate = (b[2]<<8 | b[3]>>14)+(b[2]<<8 | b[3]&((1<<14)-1))/16384.0;
	return 0;
err:
	werrstr("%s: %r", "AxisValueMap");
	return -1;
}

void
print_AxisValueMap(Otfile *f, int indent, Otf *o, AxisValueMap *v)
{
	f->print(f->aux, "%*s%s: %g\n", indent, "", "fromCoordinate", v->fromCoordinate);
	f->print(f->aux, "%*s%s: %g\n", indent, "", "toCoordinate", v->toCoordinate);
	USED(o);
}

int
read_SegmentMaps(Otf *o, SegmentMaps *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 2)) == nil)
		goto err;
	v->positionMapCount = b[0]<<8 | b[1];
	if(otfarray(o, &v->axisValueMaps, read_AxisValueMap, sizeof(AxisValueMap), v->positionMapCount) < 0){
		werrstr("%s: %r", "axisValueMaps");
		goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "SegmentMaps");
	return -1;
}

void
print_SegmentMaps(Otfile *f, int indent, Otf *o, SegmentMaps *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "positionMapCount", v->positionMapCount);
	for(int i = 0; i < v->positionMapCount; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "axisValueMaps", i);
		print_AxisValueMap(f, indent+indentΔ, o, &v->axisValueMaps[i]);
	}
	USED(o);
}

int
read_TableAvar(Otf *o, TableAvar *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 8)) == nil)
		goto err;
	u16int majorVersion = b[0]<<8 | b[1];
	if(majorVersion != 1){
		werrstr("%s: invalid value: %d (%#ux)", "majorVersion", majorVersion, majorVersion);
		goto err;
	}
	u16int minorVersion = b[2]<<8 | b[3];
	if(minorVersion != 0){
		werrstr("%s: invalid value: %d (%#ux)", "minorVersion", minorVersion, minorVersion);
		goto err;
	}
	v->axisCount = b[6]<<8 | b[7];
	if(otfarray(o, &v->axisSegmentMaps, read_SegmentMaps, sizeof(SegmentMaps), v->axisCount) < 0){
		werrstr("%s: %r", "axisSegmentMaps");
		goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "TableAvar");
	return -1;
}

void
print_TableAvar(Otfile *f, int indent, Otf *o, TableAvar *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "axisCount", v->axisCount);
	for(int i = 0; i < v->axisCount; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "axisSegmentMaps", i);
		print_SegmentMaps(f, indent+indentΔ, o, &v->axisSegmentMaps[i]);
	}
	USED(o);
}

int
read_TableOS∕2(Otf *o, TableOS∕2 *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 78)) == nil)
		goto err;
	v->version = b[0]<<8 | b[1];
	if(v->version > 5){
		werrstr("%s: invalid value: %d (%#ux)", "version", v->version, v->version);
		goto err;
	}
	v->xAvgCharWidth = b[2]<<8 | b[3];
	v->usWeightClass = b[4]<<8 | b[5];
	v->usWidthClass = b[6]<<8 | b[7];
	v->fsType = b[8]<<8 | b[9];
	v->ySubscriptXSize = b[10]<<8 | b[11];
	v->ySubscriptYSize = b[12]<<8 | b[13];
	v->ySubscriptXOffset = b[14]<<8 | b[15];
	v->ySubscriptYOffset = b[16]<<8 | b[17];
	v->ySuperscriptXSize = b[18]<<8 | b[19];
	v->ySuperscriptYSize = b[20]<<8 | b[21];
	v->ySuperscriptXOffset = b[22]<<8 | b[23];
	v->ySuperscriptYOffset = b[24]<<8 | b[25];
	v->yStrikeoutSize = b[26]<<8 | b[27];
	v->yStrikeoutPosition = b[28]<<8 | b[29];
	v->sFamilyClass = b[30]<<8 | b[31];
	for(int i = 0; i < 10; i++)
		v->panose[i] = b[32+i*1];
	v->ulUnicodeRange1 = b[42]<<24 | b[43]<<16 | b[44]<<8 | b[45];
	v->ulUnicodeRange2 = b[46]<<24 | b[47]<<16 | b[48]<<8 | b[49];
	v->ulUnicodeRange3 = b[50]<<24 | b[51]<<16 | b[52]<<8 | b[53];
	v->ulUnicodeRange4 = b[54]<<24 | b[55]<<16 | b[56]<<8 | b[57];
	v->achVendID = b[58]<<24 | b[59]<<16 | b[60]<<8 | b[61];
	v->fsSelection = b[62]<<8 | b[63];
	v->usFirstCharIndex = b[64]<<8 | b[65];
	v->usLastCharIndex = b[66]<<8 | b[67];
	v->sTypoAscender = b[68]<<8 | b[69];
	v->sTypoDescender = b[70]<<8 | b[71];
	v->sTypoLineGap = b[72]<<8 | b[73];
	v->usWinAscent = b[74]<<8 | b[75];
	v->usWinDescent = b[76]<<8 | b[77];
	if(v->version >= 1){
		if((b = otfreadn(o, 8)) == nil)
			goto err;
		v->ulCodePageRange1 = b[0]<<24 | b[1]<<16 | b[2]<<8 | b[3];
		v->ulCodePageRange2 = b[4]<<24 | b[5]<<16 | b[6]<<8 | b[7];
	}
	if(v->version >= 2){
		if((b = otfreadn(o, 10)) == nil)
			goto err;
		v->sxHeight = b[0]<<8 | b[1];
		v->sCapHeight = b[2]<<8 | b[3];
		v->usDefaultChar = b[4]<<8 | b[5];
		v->usBreakChar = b[6]<<8 | b[7];
		v->usMaxContext = b[8]<<8 | b[9];
	}
	if(v->version >= 5){
		if((b = otfreadn(o, 4)) == nil)
			goto err;
		v->usLowerOpticalPointSize = b[0]<<8 | b[1];
		v->usUpperOpticalPointSize = b[2]<<8 | b[3];
	}
	return 0;
err:
	werrstr("%s: %r", "TableOS∕2");
	return -1;
}

void
print_TableOS∕2(Otfile *f, int indent, Otf *o, TableOS∕2 *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "version", v->version);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "xAvgCharWidth", v->xAvgCharWidth);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "usWeightClass", v->usWeightClass);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "usWidthClass", v->usWidthClass);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "fsType", v->fsType);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "ySubscriptXSize", v->ySubscriptXSize);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "ySubscriptYSize", v->ySubscriptYSize);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "ySubscriptXOffset", v->ySubscriptXOffset);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "ySubscriptYOffset", v->ySubscriptYOffset);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "ySuperscriptXSize", v->ySuperscriptXSize);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "ySuperscriptYSize", v->ySuperscriptYSize);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "ySuperscriptXOffset", v->ySuperscriptXOffset);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "ySuperscriptYOffset", v->ySuperscriptYOffset);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "yStrikeoutSize", v->yStrikeoutSize);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "yStrikeoutPosition", v->yStrikeoutPosition);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "sFamilyClass", v->sFamilyClass);
	for(int i = 0; i < 10; i++)
		f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "panose", i, v->panose[i]);
	f->print(f->aux, "%*s%s: %#ux\n", indent, "", "ulUnicodeRange1", v->ulUnicodeRange1);
	f->print(f->aux, "%*s%s: %#ux\n", indent, "", "ulUnicodeRange2", v->ulUnicodeRange2);
	f->print(f->aux, "%*s%s: %#ux\n", indent, "", "ulUnicodeRange3", v->ulUnicodeRange3);
	f->print(f->aux, "%*s%s: %#ux\n", indent, "", "ulUnicodeRange4", v->ulUnicodeRange4);
	f->print(f->aux, "%*s%s: %c%c%c%c\n", indent, "", "achVendID", v->achVendID>>24, v->achVendID>>16, v->achVendID>>8, v->achVendID>>0);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "fsSelection", v->fsSelection);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "usFirstCharIndex", v->usFirstCharIndex);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "usLastCharIndex", v->usLastCharIndex);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "sTypoAscender", v->sTypoAscender);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "sTypoDescender", v->sTypoDescender);
	f->print(f->aux, "%*s%s: %d\n", indent, "", "sTypoLineGap", v->sTypoLineGap);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "usWinAscent", v->usWinAscent);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "usWinDescent", v->usWinDescent);
	if(v->version >= 1)
		f->print(f->aux, "%*s%s: %#ux\n", indent, "", "ulCodePageRange1", v->ulCodePageRange1);
	if(v->version >= 1)
		f->print(f->aux, "%*s%s: %#ux\n", indent, "", "ulCodePageRange2", v->ulCodePageRange2);
	if(v->version >= 2)
		f->print(f->aux, "%*s%s: %d\n", indent, "", "sxHeight", v->sxHeight);
	if(v->version >= 2)
		f->print(f->aux, "%*s%s: %d\n", indent, "", "sCapHeight", v->sCapHeight);
	if(v->version >= 2)
		f->print(f->aux, "%*s%s: %#ux\n", indent, "", "usDefaultChar", v->usDefaultChar);
	if(v->version >= 2)
		f->print(f->aux, "%*s%s: %#ux\n", indent, "", "usBreakChar", v->usBreakChar);
	if(v->version >= 2)
		f->print(f->aux, "%*s%s: %ud\n", indent, "", "usMaxContext", v->usMaxContext);
	if(v->version >= 5)
		f->print(f->aux, "%*s%s: %ud\n", indent, "", "usLowerOpticalPointSize", v->usLowerOpticalPointSize);
	if(v->version >= 5)
		f->print(f->aux, "%*s%s: %ud\n", indent, "", "usUpperOpticalPointSize", v->usUpperOpticalPointSize);
	USED(o);
}

int
read_BaseGlyph(Otf *o, BaseGlyph *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 6)) == nil)
		goto err;
	v->glyphID = b[0]<<8 | b[1];
	v->firstLayerIndex = b[2]<<8 | b[3];
	v->numLayers = b[4]<<8 | b[5];
	return 0;
err:
	werrstr("%s: %r", "BaseGlyph");
	return -1;
}

void
print_BaseGlyph(Otfile *f, int indent, Otf *o, BaseGlyph *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "glyphID", v->glyphID);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "firstLayerIndex", v->firstLayerIndex);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "numLayers", v->numLayers);
	USED(o);
}

int
read_Layer(Otf *o, Layer *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 4)) == nil)
		goto err;
	v->glyphID = b[0]<<8 | b[1];
	v->paletteIndex = b[2]<<8 | b[3];
	return 0;
err:
	werrstr("%s: %r", "Layer");
	return -1;
}

void
print_Layer(Otfile *f, int indent, Otf *o, Layer *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "glyphID", v->glyphID);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "paletteIndex", v->paletteIndex);
	USED(o);
}

int
read_TableCOLR(Otf *o, TableCOLR *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 14)) == nil)
		goto err;
	v->version = b[0]<<8 | b[1];
	if(v->version != 0){
		werrstr("%s: invalid value: %d (%#ux)", "version", v->version, v->version);
		goto err;
	}
	v->numBaseGlyphRecords = b[2]<<8 | b[3];
	v->baseGlyphRecordsOffset = b[4]<<24 | b[5]<<16 | b[6]<<8 | b[7];
	v->layerRecordsOffset = b[8]<<24 | b[9]<<16 | b[10]<<8 | b[11];
	v->numLayerRecords = b[12]<<8 | b[13];
	if(v->baseGlyphRecordsOffset != 0){
		if(otfpushrange(o, v->baseGlyphRecordsOffset, -1) < 0)
			goto err;
		if(otfarray(o, &v->baseGlyphs, read_BaseGlyph, sizeof(BaseGlyph), v->numBaseGlyphRecords) < 0){
			werrstr("%s: %r", "baseGlyphs");
			goto err;
		}
		if(otfpoprange(o) < 0)
			goto err;
	}
	if(v->layerRecordsOffset != 0){
		if(otfpushrange(o, v->layerRecordsOffset, -1) < 0)
			goto err;
		if(otfarray(o, &v->layers, read_Layer, sizeof(Layer), v->numLayerRecords) < 0){
			werrstr("%s: %r", "layers");
			goto err;
		}
		if(otfpoprange(o) < 0)
			goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "TableCOLR");
	return -1;
}

void
print_TableCOLR(Otfile *f, int indent, Otf *o, TableCOLR *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "version", v->version);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "numBaseGlyphRecords", v->numBaseGlyphRecords);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "baseGlyphRecordsOffset", v->baseGlyphRecordsOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "layerRecordsOffset", v->layerRecordsOffset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "numLayerRecords", v->numLayerRecords);
	for(int i = 0; i < v->numBaseGlyphRecords; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "baseGlyphs", i);
		print_BaseGlyph(f, indent+indentΔ, o, &v->baseGlyphs[i]);
	}
	for(int i = 0; i < v->numLayerRecords; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "layers", i);
		print_Layer(f, indent+indentΔ, o, &v->layers[i]);
	}
	USED(o);
}

int
read_ColorRecord(Otf *o, ColorRecord *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 4)) == nil)
		goto err;
	v->blue = b[0];
	v->greed = b[1];
	v->red = b[2];
	v->alpha = b[3];
	return 0;
err:
	werrstr("%s: %r", "ColorRecord");
	return -1;
}

void
print_ColorRecord(Otfile *f, int indent, Otf *o, ColorRecord *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "blue", v->blue);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "greed", v->greed);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "red", v->red);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "alpha", v->alpha);
	USED(o);
}

int
read_TableCPAL(Otf *o, TableCPAL *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 12)) == nil)
		goto err;
	v->version = b[0]<<8 | b[1];
	if(v->version != 0){
		werrstr("%s: invalid value: %d (%#ux)", "version", v->version, v->version);
		goto err;
	}
	v->numPaletteEntries = b[2]<<8 | b[3];
	v->numPalettes = b[4]<<8 | b[5];
	v->numColorRecords = b[6]<<8 | b[7];
	v->colorRecordsArrayOffset = b[8]<<24 | b[9]<<16 | b[10]<<8 | b[11];
	if((b = otfreadn(o, v->numPalettes*2)) == nil)
		goto err;
	v->colorRecordIndices = malloc(v->numPalettes*sizeof(*v->colorRecordIndices));
	for(int i = 0; i < v->numPalettes; i++)
		v->colorRecordIndices[i] = b[0+i*2]<<8 | b[1+i*2];
	if(v->colorRecordsArrayOffset != 0){
		if(otfpushrange(o, v->colorRecordsArrayOffset, -1) < 0)
			goto err;
		if(otfarray(o, &v->colorRecords, read_ColorRecord, sizeof(ColorRecord), v->numColorRecords) < 0){
			werrstr("%s: %r", "colorRecords");
			goto err;
		}
		if(otfpoprange(o) < 0)
			goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "TableCPAL");
	return -1;
}

void
print_TableCPAL(Otfile *f, int indent, Otf *o, TableCPAL *v)
{
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "version", v->version);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "numPaletteEntries", v->numPaletteEntries);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "numPalettes", v->numPalettes);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "numColorRecords", v->numColorRecords);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "colorRecordsArrayOffset", v->colorRecordsArrayOffset);
	for(int i = 0; i < v->numPalettes; i++)
		f->print(f->aux, "%*s%s[%d]: %ud\n", indent, "", "colorRecordIndices", i, v->colorRecordIndices[i]);
	for(int i = 0; i < v->numColorRecords; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "colorRecords", i);
		print_ColorRecord(f, indent+indentΔ, o, &v->colorRecords[i]);
	}
	USED(o);
}

int
read_TableRecord(Otf *o, TableRecord *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 16)) == nil)
		goto err;
	v->tableTag = b[0]<<24 | b[1]<<16 | b[2]<<8 | b[3];
	v->offset = b[8]<<24 | b[9]<<16 | b[10]<<8 | b[11];
	v->length = b[12]<<24 | b[13]<<16 | b[14]<<8 | b[15];
	return 0;
err:
	werrstr("%s: %r", "TableRecord");
	return -1;
}

void
print_TableRecord(Otfile *f, int indent, Otf *o, TableRecord *v)
{
	f->print(f->aux, "%*s%s: %c%c%c%c\n", indent, "", "tableTag", v->tableTag>>24, v->tableTag>>16, v->tableTag>>8, v->tableTag>>0);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "offset", v->offset);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "length", v->length);
	if(v->print != nil && v->parsed != nil)
		v->print(f, indent+indentΔ, o, v->parsed);
	USED(o);
}

int
read_TableDirectory(Otf *o, TableDirectory *v)
{
	u8int *b = nil; USED(b);
	if((b = otfreadn(o, 12)) == nil)
		goto err;
	v->sfntVersion = b[0]<<24 | b[1]<<16 | b[2]<<8 | b[3];
	if(v->sfntVersion != 0x10000 && v->sfntVersion != 0x4f54544f){
		werrstr("%s: invalid value: %d (%#ux)", "sfntVersion", v->sfntVersion, v->sfntVersion);
		goto err;
	}
	v->numTables = b[4]<<8 | b[5];
	v->searchRange = b[6]<<8 | b[7];
	v->entrySelector = b[8]<<8 | b[9];
	v->rangeShift = b[10]<<8 | b[11];
	if(otfarray(o, &v->tableRecords, read_TableRecord, sizeof(TableRecord), v->numTables) < 0){
		werrstr("%s: %r", "tableRecords");
		goto err;
	}
	int retry = 0;
	do{
		for(int i = 0; i < v->numTables; i++){
			TableRecord *rec = &v->tableRecords[i];
			if(rec->length == 0)
				continue;
			if(otfpushrange(o, rec->offset, rec->length) < 0)
				goto err;
			switch(rec->tableTag){
			case (u32int)('c'<<24|'m'<<16|'a'<<8|'p'):
				if(v->cmap != nil)
					break;
				v->cmap = calloc(1, sizeof(TableCmap));
				if(read_TableCmap(o, v->cmap) < 0){
					free(v->cmap);
					v->cmap = nil;
					goto err;
				}
				rec->parsed = v->cmap;
				rec->print = (void*)print_TableCmap;
				break;
			case (u32int)('h'<<24|'e'<<16|'a'<<8|'d'):
				if(v->head != nil)
					break;
				v->head = calloc(1, sizeof(TableHead));
				if(read_TableHead(o, v->head) < 0){
					free(v->head);
					v->head = nil;
					goto err;
				}
				rec->parsed = v->head;
				rec->print = (void*)print_TableHead;
				break;
			case (u32int)('h'<<24|'h'<<16|'e'<<8|'a'):
				if(v->hhea != nil)
					break;
				v->hhea = calloc(1, sizeof(TableHhea));
				if(read_TableHhea(o, v->hhea) < 0){
					free(v->hhea);
					v->hhea = nil;
					goto err;
				}
				rec->parsed = v->hhea;
				rec->print = (void*)print_TableHhea;
				break;
			case (u32int)('m'<<24|'a'<<16|'x'<<8|'p'):
				if(v->maxp != nil)
					break;
				v->maxp = calloc(1, sizeof(TableMaxp));
				if(read_TableMaxp(o, v->maxp) < 0){
					free(v->maxp);
					v->maxp = nil;
					goto err;
				}
				rec->parsed = v->maxp;
				rec->print = (void*)print_TableMaxp;
				break;
			case (u32int)('h'<<24|'m'<<16|'t'<<8|'x'):
				{
					static int retried = 0;
					if(v->hhea == nil || v->maxp == nil){
						if(retried){
							werrstr("%s: deps missing", "TableHmtx");
							goto err;
						}
						retried = 1;
						retry++;
						break;
					}
					if(retried)
						retry--;
				}
				if(v->hmtx != nil)
					break;
				v->hmtx = calloc(1, sizeof(TableHmtx));
				if(read_TableHmtx(o, v->hmtx) < 0){
					free(v->hmtx);
					v->hmtx = nil;
					goto err;
				}
				rec->parsed = v->hmtx;
				rec->print = (void*)print_TableHmtx;
				break;
			case (u32int)('p'<<24|'o'<<16|'s'<<8|'t'):
				if(v->post != nil)
					break;
				v->post = calloc(1, sizeof(TablePost));
				if(read_TablePost(o, v->post) < 0){
					free(v->post);
					v->post = nil;
					goto err;
				}
				rec->parsed = v->post;
				rec->print = (void*)print_TablePost;
				break;
			case (u32int)('n'<<24|'a'<<16|'m'<<8|'e'):
				if(v->name != nil)
					break;
				v->name = calloc(1, sizeof(TableName));
				if(read_TableName(o, v->name) < 0){
					free(v->name);
					v->name = nil;
					goto err;
				}
				rec->parsed = v->name;
				rec->print = (void*)print_TableName;
				break;
			case (u32int)('E'<<24|'B'<<16|'D'<<8|'T'):
				if(v->ebdt != nil)
					break;
				v->ebdt = calloc(1, sizeof(TableEBDT));
				if(read_TableEBDT(o, v->ebdt) < 0){
					free(v->ebdt);
					v->ebdt = nil;
					goto err;
				}
				rec->parsed = v->ebdt;
				rec->print = (void*)print_TableEBDT;
				break;
			case (u32int)('E'<<24|'B'<<16|'L'<<8|'C'):
				if(v->eblc != nil)
					break;
				v->eblc = calloc(1, sizeof(TableEBLC));
				if(read_TableEBLC(o, v->eblc) < 0){
					free(v->eblc);
					v->eblc = nil;
					goto err;
				}
				rec->parsed = v->eblc;
				rec->print = (void*)print_TableEBLC;
				break;
			case (u32int)('G'<<24|'D'<<16|'E'<<8|'F'):
				if(v->gdef != nil)
					break;
				v->gdef = calloc(1, sizeof(TableGDEF));
				if(read_TableGDEF(o, v->gdef) < 0){
					free(v->gdef);
					v->gdef = nil;
					goto err;
				}
				rec->parsed = v->gdef;
				rec->print = (void*)print_TableGDEF;
				break;
			case (u32int)('G'<<24|'P'<<16|'O'<<8|'S'):
				if(v->gpos != nil)
					break;
				v->gpos = calloc(1, sizeof(TableGPOS));
				if(read_TableGPOS(o, v->gpos) < 0){
					free(v->gpos);
					v->gpos = nil;
					goto err;
				}
				rec->parsed = v->gpos;
				rec->print = (void*)print_TableGPOS;
				break;
			case (u32int)('G'<<24|'S'<<16|'U'<<8|'B'):
				if(v->gsub != nil)
					break;
				v->gsub = calloc(1, sizeof(TableGSUB));
				if(read_TableGSUB(o, v->gsub) < 0){
					free(v->gsub);
					v->gsub = nil;
					goto err;
				}
				rec->parsed = v->gsub;
				rec->print = (void*)print_TableGSUB;
				break;
			case (u32int)('M'<<24|'A'<<16|'T'<<8|'H'):
				if(v->math != nil)
					break;
				v->math = calloc(1, sizeof(TableMATH));
				if(read_TableMATH(o, v->math) < 0){
					free(v->math);
					v->math = nil;
					goto err;
				}
				rec->parsed = v->math;
				rec->print = (void*)print_TableMATH;
				break;
			case (u32int)('k'<<24|'e'<<16|'r'<<8|'n'):
				if(v->kern != nil)
					break;
				v->kern = calloc(1, sizeof(TableKern));
				if(read_TableKern(o, v->kern) < 0){
					free(v->kern);
					v->kern = nil;
					goto err;
				}
				rec->parsed = v->kern;
				rec->print = (void*)print_TableKern;
				break;
			case (u32int)('l'<<24|'o'<<16|'c'<<8|'a'):
				{
					static int retried = 0;
					if(v->head == nil || v->maxp == nil){
						if(retried){
							werrstr("%s: deps missing", "TableLoca");
							goto err;
						}
						retried = 1;
						retry++;
						break;
					}
					if(retried)
						retry--;
				}
				if(v->loca != nil)
					break;
				v->loca = calloc(1, sizeof(TableLoca));
				if(read_TableLoca(o, v->loca) < 0){
					free(v->loca);
					v->loca = nil;
					break;
				}
				rec->parsed = v->loca;
				rec->print = (void*)print_TableLoca;
				break;
			case (u32int)('s'<<24|'b'<<16|'i'<<8|'x'):
				if(v->sbix != nil)
					break;
				v->sbix = calloc(1, sizeof(TableSbix));
				if(read_TableSbix(o, v->sbix) < 0){
					free(v->sbix);
					v->sbix = nil;
					goto err;
				}
				rec->parsed = v->sbix;
				rec->print = (void*)print_TableSbix;
				break;
			case (u32int)('f'<<24|'v'<<16|'a'<<8|'r'):
				if(v->fvar != nil)
					break;
				v->fvar = calloc(1, sizeof(TableFvar));
				if(read_TableFvar(o, v->fvar) < 0){
					free(v->fvar);
					v->fvar = nil;
					goto err;
				}
				rec->parsed = v->fvar;
				rec->print = (void*)print_TableFvar;
				break;
			case (u32int)('H'<<24|'V'<<16|'A'<<8|'R'):
				{
					static int retried = 0;
					if(v->fvar == nil){
						if(retried){
							werrstr("%s: deps missing", "TableHVAR");
							goto err;
						}
						retried = 1;
						retry++;
						break;
					}
					if(retried)
						retry--;
				}
				if(v->hvar != nil)
					break;
				v->hvar = calloc(1, sizeof(TableHVAR));
				if(read_TableHVAR(o, v->hvar) < 0){
					free(v->hvar);
					v->hvar = nil;
					goto err;
				}
				rec->parsed = v->hvar;
				rec->print = (void*)print_TableHVAR;
				break;
			case (u32int)('F'<<24|'F'<<16|'T'<<8|'M'):
				if(v->fftm != nil)
					break;
				v->fftm = calloc(1, sizeof(TableFFTM));
				if(read_TableFFTM(o, v->fftm) < 0){
					free(v->fftm);
					v->fftm = nil;
					goto err;
				}
				rec->parsed = v->fftm;
				rec->print = (void*)print_TableFFTM;
				break;
			case (u32int)('D'<<24|'S'<<16|'I'<<8|'G'):
				if(v->dsig != nil)
					break;
				v->dsig = calloc(1, sizeof(TableDSIG));
				if(read_TableDSIG(o, v->dsig) < 0){
					free(v->dsig);
					v->dsig = nil;
					goto err;
				}
				rec->parsed = v->dsig;
				rec->print = (void*)print_TableDSIG;
				break;
			case (u32int)('S'<<24|'T'<<16|'A'<<8|'T'):
				if(v->stat != nil)
					break;
				v->stat = calloc(1, sizeof(TableSTAT));
				if(read_TableSTAT(o, v->stat) < 0){
					free(v->stat);
					v->stat = nil;
					goto err;
				}
				rec->parsed = v->stat;
				rec->print = (void*)print_TableSTAT;
				break;
			case (u32int)('g'<<24|'a'<<16|'s'<<8|'p'):
				if(v->gasp != nil)
					break;
				v->gasp = calloc(1, sizeof(TableGasp));
				if(read_TableGasp(o, v->gasp) < 0){
					free(v->gasp);
					v->gasp = nil;
					goto err;
				}
				rec->parsed = v->gasp;
				rec->print = (void*)print_TableGasp;
				break;
			case (u32int)('g'<<24|'v'<<16|'a'<<8|'r'):
				{
					static int retried = 0;
					if(v->fvar == nil){
						if(retried){
							werrstr("%s: deps missing", "TableGvar");
							goto err;
						}
						retried = 1;
						retry++;
						break;
					}
					if(retried)
						retry--;
				}
				if(v->gvar != nil)
					break;
				v->gvar = calloc(1, sizeof(TableGvar));
				if(read_TableGvar(o, v->gvar) < 0){
					free(v->gvar);
					v->gvar = nil;
					goto err;
				}
				rec->parsed = v->gvar;
				rec->print = (void*)print_TableGvar;
				break;
			case (u32int)('a'<<24|'v'<<16|'a'<<8|'r'):
				if(v->avar != nil)
					break;
				v->avar = calloc(1, sizeof(TableAvar));
				if(read_TableAvar(o, v->avar) < 0){
					free(v->avar);
					v->avar = nil;
					goto err;
				}
				rec->parsed = v->avar;
				rec->print = (void*)print_TableAvar;
				break;
			case (u32int)('O'<<24|'S'<<16|'/'<<8|'2'):
				if(v->os∕2 != nil)
					break;
				v->os∕2 = calloc(1, sizeof(TableOS∕2));
				if(read_TableOS∕2(o, v->os∕2) < 0){
					free(v->os∕2);
					v->os∕2 = nil;
					goto err;
				}
				rec->parsed = v->os∕2;
				rec->print = (void*)print_TableOS∕2;
				break;
			case (u32int)('C'<<24|'O'<<16|'L'<<8|'R'):
				if(v->colr != nil)
					break;
				v->colr = calloc(1, sizeof(TableCOLR));
				if(read_TableCOLR(o, v->colr) < 0){
					free(v->colr);
					v->colr = nil;
					goto err;
				}
				rec->parsed = v->colr;
				rec->print = (void*)print_TableCOLR;
				break;
			case (u32int)('C'<<24|'P'<<16|'A'<<8|'L'):
				if(v->cpal != nil)
					break;
				v->cpal = calloc(1, sizeof(TableCPAL));
				if(read_TableCPAL(o, v->cpal) < 0){
					free(v->cpal);
					v->cpal = nil;
					goto err;
				}
				rec->parsed = v->cpal;
				rec->print = (void*)print_TableCPAL;
				break;
			default:
				// FIXME fprint(2, "no parser for \"%t\"\n", rec->tableTag);
				break;
			}
			if(otfpoprange(o) < 0)
				goto err;
		}
	}while(retry > 0);
	return 0;
err:
	werrstr("%s: %r", "TableDirectory");
	return -1;
}

void
print_TableDirectory(Otfile *f, int indent, Otf *o, TableDirectory *v)
{
	f->print(f->aux, "%*s%s: %#ux\n", indent, "", "sfntVersion", v->sfntVersion);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "numTables", v->numTables);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "searchRange", v->searchRange);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "entrySelector", v->entrySelector);
	f->print(f->aux, "%*s%s: %ud\n", indent, "", "rangeShift", v->rangeShift);
	for(int i = 0; i < v->numTables; i++){
		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "tableRecords", i);
		print_TableRecord(f, indent+indentΔ, o, &v->tableRecords[i]);
	}
	USED(o);
}