shithub: fnt

ref: f152a7fbaefb8b7f2dee22e4e3d54ed1afedd301
dir: /otf.c/

View raw version
/* this file is generated. do not modify. */
#include <u.h>
#include <libc.h>
#include <bio.h>
#include "otf.h"

typedef struct Range Range;

struct Otf {
	Biobuf *f;
	Range *r;
	u8int *buf;
	int bufsz;
	int off;
	/* extra fields to simplify parsing */
	s16int indexToLocFormat;
	u16int numberOfHMetrics;
	u16int numGlyphs;
	u16int firstGlyphIndex;
	u16int lastGlyphIndex;
};

struct Range {
	int start;
	int len;
	int prevoff;
	Range *par;
};

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

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

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

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

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

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

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

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

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

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

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

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

int
read_SubHeader(Otf *o, SubHeader *v)
{
	u8int *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(Biobuf *f, int indent, Otf *o, SubHeader *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "firstCode", v->firstCode);
	Bprint(f, "%*s%s: %ud\n", indent, "", "entryCode", v->entryCode);
	Bprint(f, "%*s%s: %d\n", indent, "", "idDelta", v->idDelta);
	Bprint(f, "%*s%s: %ud\n", indent, "", "idRangeOffset", v->idRangeOffset);
	USED(o);
}

int
read_MapGroup(Otf *o, MapGroup *v)
{
	u8int *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(Biobuf *f, int indent, Otf *o, MapGroup *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "startCharCode", v->startCharCode);
	Bprint(f, "%*s%s: %ud\n", indent, "", "endCharCode", v->endCharCode);
	Bprint(f, "%*s%s: %ud\n", indent, "", "startGlyphID", v->startGlyphID);
	USED(o);
}

int
read_SubtableCmap0(Otf *o, SubtableCmap0 *v)
{
	u8int *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(Biobuf *f, int indent, Otf *o, SubtableCmap0 *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "length", v->length);
	Bprint(f, "%*s%s: %ud\n", indent, "", "language", v->language);
	for(int i = 0; i < 256; i++)
		Bprint(f, "%*s%s[%d]: %ud\n", indent, "", "glyphIdArray", i, v->glyphIdArray[i]);
	USED(o);
}

int
read_SubtableCmap2(Otf *o, SubtableCmap2 *v)
{
	u8int *b;
	if((b = otfreadn(o, 516)) == 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->subHeaderKeys[i] = b[4+i*2]<<8 | b[5+i*2];
	return 0;
err:
	werrstr("%s: %r", "SubtableCmap2");
	return -1;
}

void
print_SubtableCmap2(Biobuf *f, int indent, Otf *o, SubtableCmap2 *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "length", v->length);
	Bprint(f, "%*s%s: %ud\n", indent, "", "language", v->language);
	for(int i = 0; i < 256; i++)
		Bprint(f, "%*s%s[%d]: %ud\n", indent, "", "subHeaderKeys", i, v->subHeaderKeys[i]);
	USED(o);
}

int
read_SubtableCmap4(Otf *o, SubtableCmap4 *v)
{
	u8int *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];
	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];
	return 0;
err:
	werrstr("%s: %r", "SubtableCmap4");
	return -1;
}

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

int
read_SubtableCmap6(Otf *o, SubtableCmap6 *v)
{
	u8int *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(Biobuf *f, int indent, Otf *o, SubtableCmap6 *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "length", v->length);
	Bprint(f, "%*s%s: %ud\n", indent, "", "language", v->language);
	Bprint(f, "%*s%s: %ud\n", indent, "", "firstCode", v->firstCode);
	Bprint(f, "%*s%s: %ud\n", indent, "", "entryCount", v->entryCount);
	for(int i = 0; i < v->entryCount; i++)
		Bprint(f, "%*s%s[%d]: %ud\n", indent, "", "glyphIdArray", i, v->glyphIdArray[i]);
	USED(o);
}

int
read_SubtableCmap8(Otf *o, SubtableCmap8 *v)
{
	u8int *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(Biobuf *f, int indent, Otf *o, SubtableCmap8 *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "length", v->length);
	Bprint(f, "%*s%s: %ud\n", indent, "", "language", v->language);
	for(int i = 0; i < 8192; i++)
		Bprint(f, "%*s%s[%d]: %ud\n", indent, "", "is32", i, v->is32[i]);
	Bprint(f, "%*s%s: %ud\n", indent, "", "numGroups", v->numGroups);
	for(int i = 0; i < v->numGroups; i++){
		Bprint(f, "%*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;
	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];
	return 0;
err:
	werrstr("%s: %r", "SubtableCmap10");
	return -1;
}

void
print_SubtableCmap10(Biobuf *f, int indent, Otf *o, SubtableCmap10 *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "reserved", v->reserved);
	Bprint(f, "%*s%s: %ud\n", indent, "", "length", v->length);
	Bprint(f, "%*s%s: %ud\n", indent, "", "language", v->language);
	Bprint(f, "%*s%s: %ud\n", indent, "", "startCharCode", v->startCharCode);
	Bprint(f, "%*s%s: %ud\n", indent, "", "numChars", v->numChars);
	USED(o);
}

int
read_SubtableCmap12or13(Otf *o, SubtableCmap12or13 *v)
{
	u8int *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(Biobuf *f, int indent, Otf *o, SubtableCmap12or13 *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "reserved", v->reserved);
	Bprint(f, "%*s%s: %ud\n", indent, "", "length", v->length);
	Bprint(f, "%*s%s: %ud\n", indent, "", "language", v->language);
	Bprint(f, "%*s%s: %ud\n", indent, "", "numGroups", v->numGroups);
	for(int i = 0; i < v->numGroups; i++){
		Bprint(f, "%*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;
	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(Biobuf *f, int indent, Otf *o, UnicodeRange *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "startUnicodeValue", v->startUnicodeValue);
	Bprint(f, "%*s%s: %ud\n", indent, "", "additionalCount", v->additionalCount);
	USED(o);
}

int
read_DefaultUVS(Otf *o, DefaultUVS *v)
{
	u8int *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(Biobuf *f, int indent, Otf *o, DefaultUVS *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "numUnicodeValueRanges", v->numUnicodeValueRanges);
	for(int i = 0; i < v->numUnicodeValueRanges; i++){
		Bprint(f, "%*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;
	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(Biobuf *f, int indent, Otf *o, UVSMapping *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "unicodeValue", v->unicodeValue);
	Bprint(f, "%*s%s: %ud\n", indent, "", "glyphID", v->glyphID);
	USED(o);
}

int
read_NonDefaultUVS(Otf *o, NonDefaultUVS *v)
{
	u8int *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(Biobuf *f, int indent, Otf *o, NonDefaultUVS *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "numUVSMappings", v->numUVSMappings);
	for(int i = 0; i < v->numUVSMappings; i++){
		Bprint(f, "%*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;
	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(Biobuf *f, int indent, Otf *o, VariationSelector *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "varSelector", v->varSelector);
	Bprint(f, "%*s%s: %ud\n", indent, "", "defaultUVSOffset", v->defaultUVSOffset);
	Bprint(f, "%*s%s: %ud\n", indent, "", "nonDefaultUVSOffset", v->nonDefaultUVSOffset);
	Bprint(f, "%*s%s:\n", indent, "", "defaultUVS");
	if(v->defaultUVS != nil)
		print_DefaultUVS(f, indent+indentΔ, o, v->defaultUVS);
	Bprint(f, "%*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;
	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(Biobuf *f, int indent, Otf *o, SubtableCmap14 *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "length", v->length);
	Bprint(f, "%*s%s: %ud\n", indent, "", "numVarSelectorRecords", v->numVarSelectorRecords);
	for(int i = 0; i < v->numVarSelectorRecords; i++){
		Bprint(f, "%*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;
	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 (0x%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(Biobuf *f, int indent, Otf *o, SubtableCmap *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "format", v->format);
	if(v->format == 0){
		Bprint(f, "%*s%s:\n", indent, "", "sub0");
		print_SubtableCmap0(f, indent+indentΔ, o, &v->sub0);
	}
	if(v->format == 2){
		Bprint(f, "%*s%s:\n", indent, "", "sub2");
		print_SubtableCmap2(f, indent+indentΔ, o, &v->sub2);
	}
	if(v->format == 4){
		Bprint(f, "%*s%s:\n", indent, "", "sub4");
		print_SubtableCmap4(f, indent+indentΔ, o, &v->sub4);
	}
	if(v->format == 6){
		Bprint(f, "%*s%s:\n", indent, "", "sub6");
		print_SubtableCmap6(f, indent+indentΔ, o, &v->sub6);
	}
	if(v->format == 8){
		Bprint(f, "%*s%s:\n", indent, "", "sub8");
		print_SubtableCmap8(f, indent+indentΔ, o, &v->sub8);
	}
	if(v->format == 10){
		Bprint(f, "%*s%s:\n", indent, "", "sub10");
		print_SubtableCmap10(f, indent+indentΔ, o, &v->sub10);
	}
	if(v->format == 12 || v->format == 13){
		Bprint(f, "%*s%s:\n", indent, "", "sub12or13");
		print_SubtableCmap12or13(f, indent+indentΔ, o, &v->sub12or13);
	}
	if(v->format == 14){
		Bprint(f, "%*s%s:\n", indent, "", "sub14");
		print_SubtableCmap14(f, indent+indentΔ, o, &v->sub14);
	}
	USED(o);
}

int
read_EncodingRecord(Otf *o, EncodingRecord *v)
{
	u8int *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 (0x%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(Biobuf *f, int indent, Otf *o, EncodingRecord *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "platformID", v->platformID);
	Bprint(f, "%*s%s: %ud\n", indent, "", "encodingID", v->encodingID);
	Bprint(f, "%*s%s: %ud\n", indent, "", "subtableOffset", v->subtableOffset);
	Bprint(f, "%*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;
	if((b = otfreadn(o, 4)) == nil)
		goto err;
	u16int version = b[0]<<8 | b[1];
	if(version != 0){
		werrstr("%s: invalid value: %d (0x%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;
	}
	return 0;
err:
	werrstr("%s: %r", "TableCmap");
	return -1;
}

void
print_TableCmap(Biobuf *f, int indent, Otf *o, TableCmap *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "numTables", v->numTables);
	for(int i = 0; i < v->numTables; i++){
		Bprint(f, "%*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;
	if((b = otfreadn(o, 54)) == nil)
		goto err;
	u16int majorVersion = b[0]<<8 | b[1];
	if(majorVersion != 1){
		werrstr("%s: invalid value: %d (0x%ux)", "majorVersion", majorVersion, majorVersion);
		goto err;
	}
	u16int minorVersion = b[2]<<8 | b[3];
	if(minorVersion != 0){
		werrstr("%s: invalid value: %d (0x%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 (0x%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 (0x%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 (0x%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 (0x%ux)", "indexToLocFormat", v->indexToLocFormat, v->indexToLocFormat);
		goto err;
	}
	s16int glyphDataFormat = b[52]<<8 | b[53];
	if(glyphDataFormat != 0){
		werrstr("%s: invalid value: %d (0x%ux)", "glyphDataFormat", glyphDataFormat, glyphDataFormat);
		goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "TableHead");
	return -1;
}

void
print_TableHead(Biobuf *f, int indent, Otf *o, TableHead *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "flags", v->flags);
	Bprint(f, "%*s%s: %ud\n", indent, "", "unitsPerEm", v->unitsPerEm);
	Bprint(f, "%*s%s: %T\n", indent, "", "created", v->created);
	Bprint(f, "%*s%s: %T\n", indent, "", "modified", v->modified);
	Bprint(f, "%*s%s: %d\n", indent, "", "xMin", v->xMin);
	Bprint(f, "%*s%s: %d\n", indent, "", "yMin", v->yMin);
	Bprint(f, "%*s%s: %d\n", indent, "", "xMax", v->xMax);
	Bprint(f, "%*s%s: %d\n", indent, "", "yMax", v->yMax);
	Bprint(f, "%*s%s: %ud\n", indent, "", "macStyle", v->macStyle);
	Bprint(f, "%*s%s: %ud\n", indent, "", "lowestRecPPEM", v->lowestRecPPEM);
	Bprint(f, "%*s%s: %d\n", indent, "", "indexToLocFormat", v->indexToLocFormat);
	USED(o);
}

int
read_TableHhea(Otf *o, TableHhea *v)
{
	u8int *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 (0x%ux)", "majorVersion", v->majorVersion, v->majorVersion);
		goto err;
	}
	v->minorVersion = b[2]<<8 | b[3];
	if(v->minorVersion != 0){
		werrstr("%s: invalid value: %d (0x%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 (0x%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(Biobuf *f, int indent, Otf *o, TableHhea *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "majorVersion", v->majorVersion);
	Bprint(f, "%*s%s: %ud\n", indent, "", "minorVersion", v->minorVersion);
	Bprint(f, "%*s%s: %d\n", indent, "", "ascender", v->ascender);
	Bprint(f, "%*s%s: %d\n", indent, "", "descender", v->descender);
	Bprint(f, "%*s%s: %d\n", indent, "", "lineGap", v->lineGap);
	Bprint(f, "%*s%s: %ud\n", indent, "", "advanceWidthMax", v->advanceWidthMax);
	Bprint(f, "%*s%s: %d\n", indent, "", "minLeftSideBearing", v->minLeftSideBearing);
	Bprint(f, "%*s%s: %d\n", indent, "", "minRightSideBearing", v->minRightSideBearing);
	Bprint(f, "%*s%s: %d\n", indent, "", "xMaxExtent", v->xMaxExtent);
	Bprint(f, "%*s%s: %d\n", indent, "", "caretSlopeRise", v->caretSlopeRise);
	Bprint(f, "%*s%s: %d\n", indent, "", "caretSlopeRun", v->caretSlopeRun);
	Bprint(f, "%*s%s: %d\n", indent, "", "caretOffset", v->caretOffset);
	Bprint(f, "%*s%s: %d\n", indent, "", "metricDataFormat", v->metricDataFormat);
	Bprint(f, "%*s%s: %ud\n", indent, "", "numberOfHMetrics", v->numberOfHMetrics);
	USED(o);
}

int
read_LongHorMetric(Otf *o, LongHorMetric *v)
{
	u8int *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(Biobuf *f, int indent, Otf *o, LongHorMetric *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "advanceWidth", v->advanceWidth);
	Bprint(f, "%*s%s: %d\n", indent, "", "lsb", v->lsb);
	USED(o);
}

int
read_TableMaxp(Otf *o, TableMaxp *v)
{
	u8int *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 (0x%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(Biobuf *f, int indent, Otf *o, TableMaxp *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "numGlyphs", v->numGlyphs);
	USED(o);
}

int
read_TableHmtx(Otf *o, TableHmtx *v)
{
	u8int *b;
	if(otfarray(o, &v->hMetrics, read_LongHorMetric, sizeof(LongHorMetric), (o->numberOfHMetrics+0)) < 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(Biobuf *f, int indent, Otf *o, TableHmtx *v)
{
	for(int i = 0; i < (o->numberOfHMetrics+0); i++){
		Bprint(f, "%*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++)
		Bprint(f, "%*s%s[%d]: %d\n", indent, "", "leftSideBearings", i, v->leftSideBearings[i]);
	USED(o);
}

int
read_TablePost(Otf *o, TablePost *v)
{
	u8int *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 (0x%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(Biobuf *f, int indent, Otf *o, TablePost *v)
{
	Bprint(f, "%*s%s: %g\n", indent, "", "italicAngle", v->italicAngle);
	Bprint(f, "%*s%s: %d\n", indent, "", "underlinePosition", v->underlinePosition);
	Bprint(f, "%*s%s: %d\n", indent, "", "underlineThickness", v->underlineThickness);
	Bprint(f, "%*s%s: %ud\n", indent, "", "isFixedPitch", v->isFixedPitch);
	USED(o);
}

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

void
print_NameRecord(Biobuf *f, int indent, Otf *o, NameRecord *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "platformID", v->platformID);
	Bprint(f, "%*s%s: %ud\n", indent, "", "encodingID", v->encodingID);
	Bprint(f, "%*s%s: %ud\n", indent, "", "languageID", v->languageID);
	Bprint(f, "%*s%s: %ud\n", indent, "", "nameID", v->nameID);
	Bprint(f, "%*s%s: %ud\n", indent, "", "length", v->length);
	Bprint(f, "%*s%s: %ud\n", indent, "", "stringOffset", v->stringOffset);
	USED(o);
}

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

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

int
read_TableName(Otf *o, TableName *v)
{
	u8int *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 (0x%ux)", "version", v->version, v->version);
		goto err;
	}
	v->count = b[2]<<8 | b[3];
	v->storageOffset = b[4]<<8 | b[5];
	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(Biobuf *f, int indent, Otf *o, TableName *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "version", v->version);
	Bprint(f, "%*s%s: %ud\n", indent, "", "count", v->count);
	Bprint(f, "%*s%s: %ud\n", indent, "", "storageOffset", v->storageOffset);
	for(int i = 0; i < v->count; i++){
		Bprint(f, "%*s%s[%d]:\n", indent, "", "nameRecord", i);
		print_NameRecord(f, indent+indentΔ, o, &v->nameRecord[i]);
	}
	if(v->version >= 1)
		Bprint(f, "%*s%s: %ud\n", indent, "", "langTagCount", v->langTagCount);
	if(v->version >= 1){
		for(int i = 0; i < v->langTagCount; i++){
			Bprint(f, "%*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;
	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(Biobuf *f, int indent, Otf *o, BigGlyphMetrics *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "height", v->height);
	Bprint(f, "%*s%s: %ud\n", indent, "", "width", v->width);
	Bprint(f, "%*s%s: %d\n", indent, "", "horiBearingX", v->horiBearingX);
	Bprint(f, "%*s%s: %d\n", indent, "", "horiBearingY", v->horiBearingY);
	Bprint(f, "%*s%s: %ud\n", indent, "", "horiAdvance", v->horiAdvance);
	Bprint(f, "%*s%s: %d\n", indent, "", "vertBearingX", v->vertBearingX);
	Bprint(f, "%*s%s: %d\n", indent, "", "vertBearingY", v->vertBearingY);
	Bprint(f, "%*s%s: %ud\n", indent, "", "vertAdvance", v->vertAdvance);
	USED(o);
}

int
read_SmallGlyphMetrics(Otf *o, SmallGlyphMetrics *v)
{
	u8int *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(Biobuf *f, int indent, Otf *o, SmallGlyphMetrics *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "height", v->height);
	Bprint(f, "%*s%s: %ud\n", indent, "", "width", v->width);
	Bprint(f, "%*s%s: %d\n", indent, "", "bearingX", v->bearingX);
	Bprint(f, "%*s%s: %d\n", indent, "", "bearingY", v->bearingY);
	Bprint(f, "%*s%s: %ud\n", indent, "", "advance", v->advance);
	USED(o);
}

int
read_SbitLineMetrics(Otf *o, SbitLineMetrics *v)
{
	u8int *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(Biobuf *f, int indent, Otf *o, SbitLineMetrics *v)
{
	Bprint(f, "%*s%s: %d\n", indent, "", "ascender", v->ascender);
	Bprint(f, "%*s%s: %d\n", indent, "", "descender", v->descender);
	Bprint(f, "%*s%s: %ud\n", indent, "", "widthMax", v->widthMax);
	Bprint(f, "%*s%s: %d\n", indent, "", "caretSlopeNumerator", v->caretSlopeNumerator);
	Bprint(f, "%*s%s: %d\n", indent, "", "caretSlopeDenumerator", v->caretSlopeDenumerator);
	Bprint(f, "%*s%s: %d\n", indent, "", "caretOffset", v->caretOffset);
	Bprint(f, "%*s%s: %d\n", indent, "", "minOriginSB", v->minOriginSB);
	Bprint(f, "%*s%s: %d\n", indent, "", "minAdvanceSB", v->minAdvanceSB);
	Bprint(f, "%*s%s: %d\n", indent, "", "maxBeforeBL", v->maxBeforeBL);
	Bprint(f, "%*s%s: %d\n", indent, "", "minAfterBL", v->minAfterBL);
	USED(o);
}

int
read_IndexSubtable1(Otf *o, IndexSubtable1 *v)
{
	u8int *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(Biobuf *f, int indent, Otf *o, IndexSubtable1 *v)
{
	for(int i = 0; i < ((o->lastGlyphIndex-o->firstGlyphIndex)+2); i++)
		Bprint(f, "%*s%s[%d]: %ud\n", indent, "", "sbitOffsets", i, v->sbitOffsets[i]);
	USED(o);
}

int
read_IndexSubtable2(Otf *o, IndexSubtable2 *v)
{
	u8int *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(Biobuf *f, int indent, Otf *o, IndexSubtable2 *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "imageSize", v->imageSize);
	Bprint(f, "%*s%s:\n", indent, "", "bigMetrics");
	print_BigGlyphMetrics(f, indent+indentΔ, o, &v->bigMetrics);
	USED(o);
}

int
read_IndexSubtable3(Otf *o, IndexSubtable3 *v)
{
	u8int *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(Biobuf *f, int indent, Otf *o, IndexSubtable3 *v)
{
	for(int i = 0; i < ((o->lastGlyphIndex-o->firstGlyphIndex)+2); i++)
		Bprint(f, "%*s%s[%d]: %ud\n", indent, "", "sbitOffsets", i, v->sbitOffsets[i]);
	USED(o);
}

int
read_GlyphIdOffsetPair(Otf *o, GlyphIdOffsetPair *v)
{
	u8int *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(Biobuf *f, int indent, Otf *o, GlyphIdOffsetPair *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "glyphID", v->glyphID);
	Bprint(f, "%*s%s: %ud\n", indent, "", "sbitOffset", v->sbitOffset);
	USED(o);
}

int
read_IndexSubtable4(Otf *o, IndexSubtable4 *v)
{
	u8int *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(Biobuf *f, int indent, Otf *o, IndexSubtable4 *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "numGlyphs", v->numGlyphs);
	for(int i = 0; i < (v->numGlyphs+1); i++){
		Bprint(f, "%*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;
	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(Biobuf *f, int indent, Otf *o, IndexSubtable5 *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "imageSize", v->imageSize);
	Bprint(f, "%*s%s:\n", indent, "", "bigMetrics");
	print_BigGlyphMetrics(f, indent+indentΔ, o, &v->bigMetrics);
	Bprint(f, "%*s%s: %ud\n", indent, "", "numGlyphs", v->numGlyphs);
	for(int i = 0; i < v->numGlyphs; i++)
		Bprint(f, "%*s%s[%d]: %ud\n", indent, "", "glyphIdArray", i, v->glyphIdArray[i]);
	USED(o);
}

int
read_IndexSubtable(Otf *o, IndexSubtable *v)
{
	u8int *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 (0x%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(Biobuf *f, int indent, Otf *o, IndexSubtable *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "indexFormat", v->indexFormat);
	Bprint(f, "%*s%s: %ud\n", indent, "", "imageFormat", v->imageFormat);
	Bprint(f, "%*s%s: %ud\n", indent, "", "imageDataOffset", v->imageDataOffset);
	if(v->indexFormat == 1){
		Bprint(f, "%*s%s:\n", indent, "", "sub1");
		print_IndexSubtable1(f, indent+indentΔ, o, &v->sub1);
	}
	if(v->indexFormat == 2){
		Bprint(f, "%*s%s:\n", indent, "", "sub2");
		print_IndexSubtable2(f, indent+indentΔ, o, &v->sub2);
	}
	if(v->indexFormat == 3){
		Bprint(f, "%*s%s:\n", indent, "", "sub3");
		print_IndexSubtable3(f, indent+indentΔ, o, &v->sub3);
	}
	if(v->indexFormat == 4){
		Bprint(f, "%*s%s:\n", indent, "", "sub4");
		print_IndexSubtable4(f, indent+indentΔ, o, &v->sub4);
	}
	if(v->indexFormat == 5){
		Bprint(f, "%*s%s:\n", indent, "", "sub5");
		print_IndexSubtable5(f, indent+indentΔ, o, &v->sub5);
	}
	USED(o);
}

int
read_IndexSubtableRecord(Otf *o, IndexSubtableRecord *v)
{
	u8int *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(Biobuf *f, int indent, Otf *o, IndexSubtableRecord *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "firstGlyphIndex", v->firstGlyphIndex);
	Bprint(f, "%*s%s: %ud\n", indent, "", "lastGlyphIndex", v->lastGlyphIndex);
	Bprint(f, "%*s%s: %ud\n", indent, "", "indexSubtableOffset", v->indexSubtableOffset);
	Bprint(f, "%*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;
	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];
	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(Biobuf *f, int indent, Otf *o, BitmapSize *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "indexSubtableListOffset", v->indexSubtableListOffset);
	Bprint(f, "%*s%s: %ud\n", indent, "", "indexSubtableListSize", v->indexSubtableListSize);
	Bprint(f, "%*s%s: %ud\n", indent, "", "numberOfIndexSubtables", v->numberOfIndexSubtables);
	Bprint(f, "%*s%s:\n", indent, "", "hori");
	print_SbitLineMetrics(f, indent+indentΔ, o, &v->hori);
	Bprint(f, "%*s%s:\n", indent, "", "vert");
	print_SbitLineMetrics(f, indent+indentΔ, o, &v->vert);
	Bprint(f, "%*s%s: %ud\n", indent, "", "startGlyphIndex", v->startGlyphIndex);
	Bprint(f, "%*s%s: %ud\n", indent, "", "endGlyphIndex", v->endGlyphIndex);
	Bprint(f, "%*s%s: %ud\n", indent, "", "ppemX", v->ppemX);
	Bprint(f, "%*s%s: %ud\n", indent, "", "ppemY", v->ppemY);
	Bprint(f, "%*s%s: %ud\n", indent, "", "bitDepth", v->bitDepth);
	Bprint(f, "%*s%s: %d\n", indent, "", "flags", v->flags);
	for(int i = 0; i < v->numberOfIndexSubtables; i++){
		Bprint(f, "%*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;
	if((b = otfreadn(o, 4)) == nil)
		goto err;
	v->majorVersion = b[0]<<8 | b[1];
	if(v->majorVersion != 2){
		werrstr("%s: invalid value: %d (0x%ux)", "majorVersion", v->majorVersion, v->majorVersion);
		goto err;
	}
	u16int minorVersion = b[2]<<8 | b[3];
	if(minorVersion != 0){
		werrstr("%s: invalid value: %d (0x%ux)", "minorVersion", minorVersion, minorVersion);
		goto err;
	}
	return 0;
err:
	werrstr("%s: %r", "TableEBDT");
	return -1;
}

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

int
read_TableEBLC(Otf *o, TableEBLC *v)
{
	u8int *b;
	if((b = otfreadn(o, 8)) == nil)
		goto err;
	u16int majorVersion = b[0]<<8 | b[1];
	if(majorVersion != 2){
		werrstr("%s: invalid value: %d (0x%ux)", "majorVersion", majorVersion, majorVersion);
		goto err;
	}
	u16int minorVersion = b[2]<<8 | b[3];
	if(minorVersion != 0){
		werrstr("%s: invalid value: %d (0x%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(Biobuf *f, int indent, Otf *o, TableEBLC *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "numSizes", v->numSizes);
	for(int i = 0; i < v->numSizes; i++){
		Bprint(f, "%*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;
	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(Biobuf *f, int indent, Otf *o, AttachList *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "coverageOffset", v->coverageOffset);
	Bprint(f, "%*s%s: %ud\n", indent, "", "glyphCount", v->glyphCount);
	for(int i = 0; i < v->glyphCount; i++)
		Bprint(f, "%*s%s[%d]: %ud\n", indent, "", "attachPointOffsets", i, v->attachPointOffsets[i]);
	USED(o);
}

int
read_AttachPoint(Otf *o, AttachPoint *v)
{
	u8int *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(Biobuf *f, int indent, Otf *o, AttachPoint *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "pointCount", v->pointCount);
	for(int i = 0; i < v->pointCount; i++)
		Bprint(f, "%*s%s[%d]: %ud\n", indent, "", "pointIndices", i, v->pointIndices[i]);
	USED(o);
}

int
read_LigCaretList(Otf *o, LigCaretList *v)
{
	u8int *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(Biobuf *f, int indent, Otf *o, LigCaretList *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "coverageOffset", v->coverageOffset);
	Bprint(f, "%*s%s: %ud\n", indent, "", "ligGlyphCount", v->ligGlyphCount);
	for(int i = 0; i < v->ligGlyphCount; i++)
		Bprint(f, "%*s%s[%d]: %ud\n", indent, "", "ligGlyphOffsets", i, v->ligGlyphOffsets[i]);
	USED(o);
}

int
read_LigGlyph(Otf *o, LigGlyph *v)
{
	u8int *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(Biobuf *f, int indent, Otf *o, LigGlyph *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "caretCount", v->caretCount);
	for(int i = 0; i < v->caretCount; i++)
		Bprint(f, "%*s%s[%d]: %ud\n", indent, "", "caretValueOffsets", i, v->caretValueOffsets[i]);
	USED(o);
}

int
read_CaretValue(Otf *o, CaretValue *v)
{
	u8int *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 (0x%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(Biobuf *f, int indent, Otf *o, CaretValue *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "format", v->format);
	if(v->format == 1 || v->format == 3)
		Bprint(f, "%*s%s: %d\n", indent, "", "coordinate", v->coordinate);
	if(v->format == 2)
		Bprint(f, "%*s%s: %ud\n", indent, "", "caretValuePointIndex", v->caretValuePointIndex);
	if(v->format == 3)
		Bprint(f, "%*s%s: %ud\n", indent, "", "deviceOffset", v->deviceOffset);
	USED(o);
}

int
read_ValueRecord(Otf *o, ValueRecord *v)
{
	u8int *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(Biobuf *f, int indent, Otf *o, ValueRecord *v)
{
	Bprint(f, "%*s%s: %d\n", indent, "", "xPlacement", v->xPlacement);
	Bprint(f, "%*s%s: %d\n", indent, "", "yPlacement", v->yPlacement);
	Bprint(f, "%*s%s: %d\n", indent, "", "xAdvance", v->xAdvance);
	Bprint(f, "%*s%s: %d\n", indent, "", "yAdvance", v->yAdvance);
	Bprint(f, "%*s%s: %ud\n", indent, "", "xPlaDeviceOffset", v->xPlaDeviceOffset);
	Bprint(f, "%*s%s: %ud\n", indent, "", "yPlaDeviceOffset", v->yPlaDeviceOffset);
	Bprint(f, "%*s%s: %ud\n", indent, "", "xAdvDeviceOffset", v->xAdvDeviceOffset);
	Bprint(f, "%*s%s: %ud\n", indent, "", "yAdvDeviceOffset", v->yAdvDeviceOffset);
	USED(o);
}

int
read_SinglePos(Otf *o, SinglePos *v)
{
	u8int *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 (0x%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(Biobuf *f, int indent, Otf *o, SinglePos *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "format", v->format);
	Bprint(f, "%*s%s: %ud\n", indent, "", "coverageOffset", v->coverageOffset);
	Bprint(f, "%*s%s: %ud\n", indent, "", "valueFormat", v->valueFormat);
	if(v->format == 1){
		Bprint(f, "%*s%s:\n", indent, "", "valueRecord");
		print_ValueRecord(f, indent+indentΔ, o, &v->valueRecord);
	}
	if(v->format == 2)
		Bprint(f, "%*s%s: %ud\n", indent, "", "valueCount", v->valueCount);
	if(v->format == 2){
		for(int i = 0; i < v->valueCount; i++){
			Bprint(f, "%*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;
	if((b = otfreadn(o, 12)) == nil)
		goto err;
	u16int majorVersion = b[0]<<8 | b[1];
	if(majorVersion != 1){
		werrstr("%s: invalid value: %d (0x%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 (0x%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(Biobuf *f, int indent, Otf *o, TableGDEF *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "minorVersion", v->minorVersion);
	Bprint(f, "%*s%s: %ud\n", indent, "", "glyphClassDefOffset", v->glyphClassDefOffset);
	Bprint(f, "%*s%s: %ud\n", indent, "", "attachListOffset", v->attachListOffset);
	Bprint(f, "%*s%s: %ud\n", indent, "", "ligCaretListOffset", v->ligCaretListOffset);
	Bprint(f, "%*s%s: %ud\n", indent, "", "markAttachClassDefOffset", v->markAttachClassDefOffset);
	if(v->minorVersion >= 2)
		Bprint(f, "%*s%s: %ud\n", indent, "", "markGlyphSetsDefOffset", v->markGlyphSetsDefOffset);
	if(v->minorVersion >= 3)
		Bprint(f, "%*s%s: %ud\n", indent, "", "itemVarStoreOffset", v->itemVarStoreOffset);
	USED(o);
}

int
read_LangSys(Otf *o, LangSys *v)
{
	u8int *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(Biobuf *f, int indent, Otf *o, LangSys *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "requiredFeatureIndex", v->requiredFeatureIndex);
	Bprint(f, "%*s%s: %ud\n", indent, "", "featureIndexCount", v->featureIndexCount);
	for(int i = 0; i < v->featureIndexCount; i++)
		Bprint(f, "%*s%s[%d]: %ud\n", indent, "", "featureIndices", i, v->featureIndices[i]);
	USED(o);
}

int
read_LangSysRecord(Otf *o, LangSysRecord *v)
{
	u8int *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(Biobuf *f, int indent, Otf *o, LangSysRecord *v)
{
	Bprint(f, "%*s%s: %t\n", indent, "", "langSysTag", v->langSysTag);
	Bprint(f, "%*s%s: %ud\n", indent, "", "langSysOffset", v->langSysOffset);
	Bprint(f, "%*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;
	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(Biobuf *f, int indent, Otf *o, Script *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "defaultLangSysOffset", v->defaultLangSysOffset);
	Bprint(f, "%*s%s: %ud\n", indent, "", "langSysCount", v->langSysCount);
	for(int i = 0; i < v->langSysCount; i++){
		Bprint(f, "%*s%s[%d]:\n", indent, "", "langSysRecords", i);
		print_LangSysRecord(f, indent+indentΔ, o, &v->langSysRecords[i]);
	}
	Bprint(f, "%*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;
	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(Biobuf *f, int indent, Otf *o, ScriptRecord *v)
{
	Bprint(f, "%*s%s: %t\n", indent, "", "scriptTag", v->scriptTag);
	Bprint(f, "%*s%s: %ud\n", indent, "", "scriptOffset", v->scriptOffset);
	Bprint(f, "%*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;
	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(Biobuf *f, int indent, Otf *o, ScriptList *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "scriptCount", v->scriptCount);
	for(int i = 0; i < v->scriptCount; i++){
		Bprint(f, "%*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;
	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(Biobuf *f, int indent, Otf *o, Feature *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "featureParamsOffset", v->featureParamsOffset);
	Bprint(f, "%*s%s: %ud\n", indent, "", "lookupIndexCount", v->lookupIndexCount);
	for(int i = 0; i < v->lookupIndexCount; i++)
		Bprint(f, "%*s%s[%d]: %ud\n", indent, "", "lookupListIndices", i, v->lookupListIndices[i]);
	USED(o);
}

int
read_FeatureRecord(Otf *o, FeatureRecord *v)
{
	u8int *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(Biobuf *f, int indent, Otf *o, FeatureRecord *v)
{
	Bprint(f, "%*s%s: %t\n", indent, "", "featureTag", v->featureTag);
	Bprint(f, "%*s%s: %ud\n", indent, "", "featureOffset", v->featureOffset);
	Bprint(f, "%*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;
	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(Biobuf *f, int indent, Otf *o, FeatureList *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "featureCount", v->featureCount);
	for(int i = 0; i < v->featureCount; i++){
		Bprint(f, "%*s%s[%d]:\n", indent, "", "featureRecords", i);
		print_FeatureRecord(f, indent+indentΔ, o, &v->featureRecords[i]);
	}
	USED(o);
}

int
read_Lookup(Otf *o, Lookup *v)
{
	u8int *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];
	return 0;
err:
	werrstr("%s: %r", "Lookup");
	return -1;
}

void
print_Lookup(Biobuf *f, int indent, Otf *o, Lookup *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "lookupType", v->lookupType);
	Bprint(f, "%*s%s: %ud\n", indent, "", "lookupFlag", v->lookupFlag);
	Bprint(f, "%*s%s: %ud\n", indent, "", "subTableCount", v->subTableCount);
	for(int i = 0; i < v->subTableCount; i++)
		Bprint(f, "%*s%s[%d]: %ud\n", indent, "", "subtableOffsets", i, v->subtableOffsets[i]);
	Bprint(f, "%*s%s: %ud\n", indent, "", "markFilteringSet", v->markFilteringSet);
	USED(o);
}

int
read_LookupList(Otf *o, LookupList *v)
{
	u8int *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];
	return 0;
err:
	werrstr("%s: %r", "LookupList");
	return -1;
}

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

int
read_TableGPOS(Otf *o, TableGPOS *v)
{
	u8int *b;
	if((b = otfreadn(o, 10)) == nil)
		goto err;
	u16int majorVersion = b[0]<<8 | b[1];
	if(majorVersion != 1){
		werrstr("%s: invalid value: %d (0x%ux)", "majorVersion", majorVersion, majorVersion);
		goto err;
	}
	v->minorVersion = b[2]<<8 | b[3];
	if(v->minorVersion > 1){
		werrstr("%s: invalid value: %d (0x%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(Biobuf *f, int indent, Otf *o, TableGPOS *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "minorVersion", v->minorVersion);
	Bprint(f, "%*s%s: %ud\n", indent, "", "scriptListOffset", v->scriptListOffset);
	Bprint(f, "%*s%s: %ud\n", indent, "", "featureListOffset", v->featureListOffset);
	Bprint(f, "%*s%s: %ud\n", indent, "", "lookupListOffset", v->lookupListOffset);
	if(v->minorVersion == 1)
		Bprint(f, "%*s%s: %ud\n", indent, "", "featureVariationsOffset", v->featureVariationsOffset);
	Bprint(f, "%*s%s:\n", indent, "", "scriptList");
	if(v->scriptList != nil)
		print_ScriptList(f, indent+indentΔ, o, v->scriptList);
	Bprint(f, "%*s%s:\n", indent, "", "featureList");
	if(v->featureList != nil)
		print_FeatureList(f, indent+indentΔ, o, v->featureList);
	Bprint(f, "%*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;
	if((b = otfreadn(o, 10)) == nil)
		goto err;
	u16int majorVersion = b[0]<<8 | b[1];
	if(majorVersion != 1){
		werrstr("%s: invalid value: %d (0x%ux)", "majorVersion", majorVersion, majorVersion);
		goto err;
	}
	v->minorVersion = b[2]<<8 | b[3];
	if(v->minorVersion > 1){
		werrstr("%s: invalid value: %d (0x%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(Biobuf *f, int indent, Otf *o, TableGSUB *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "minorVersion", v->minorVersion);
	Bprint(f, "%*s%s: %ud\n", indent, "", "scriptListOffset", v->scriptListOffset);
	Bprint(f, "%*s%s: %ud\n", indent, "", "featureListOffset", v->featureListOffset);
	Bprint(f, "%*s%s: %ud\n", indent, "", "lookupListOffset", v->lookupListOffset);
	if(v->minorVersion == 1)
		Bprint(f, "%*s%s: %ud\n", indent, "", "featureVariationsOffset", v->featureVariationsOffset);
	Bprint(f, "%*s%s:\n", indent, "", "scriptList");
	if(v->scriptList != nil)
		print_ScriptList(f, indent+indentΔ, o, v->scriptList);
	Bprint(f, "%*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;
	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(Biobuf *f, int indent, Otf *o, MathValueRecord *v)
{
	Bprint(f, "%*s%s: %d\n", indent, "", "value", v->value);
	Bprint(f, "%*s%s: %ud\n", indent, "", "deviceOffset", v->deviceOffset);
	USED(o);
}

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

int
read_MathItalicsCorrectionInfo(Otf *o, MathItalicsCorrectionInfo *v)
{
	u8int *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(Biobuf *f, int indent, Otf *o, MathItalicsCorrectionInfo *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "italicsCorrectionCoverageOffset", v->italicsCorrectionCoverageOffset);
	Bprint(f, "%*s%s: %ud\n", indent, "", "italicsCorrectionCount", v->italicsCorrectionCount);
	for(int i = 0; i < v->italicsCorrectionCount; i++){
		Bprint(f, "%*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;
	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(Biobuf *f, int indent, Otf *o, MathTopAccentAttachment *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "topAccentCoverageOffset", v->topAccentCoverageOffset);
	Bprint(f, "%*s%s: %ud\n", indent, "", "topAccentAttachmentCount", v->topAccentAttachmentCount);
	for(int i = 0; i < v->topAccentAttachmentCount; i++){
		Bprint(f, "%*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;
	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(Biobuf *f, int indent, Otf *o, MathKernInfoRecord *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "topRightMathKernOffset", v->topRightMathKernOffset);
	Bprint(f, "%*s%s: %ud\n", indent, "", "topLeftMathKernOffset", v->topLeftMathKernOffset);
	Bprint(f, "%*s%s: %ud\n", indent, "", "bottomRightMathKernOffset", v->bottomRightMathKernOffset);
	Bprint(f, "%*s%s: %ud\n", indent, "", "bottomLeftMathKernOffset", v->bottomLeftMathKernOffset);
	USED(o);
}

int
read_MathKernInfo(Otf *o, MathKernInfo *v)
{
	u8int *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(Biobuf *f, int indent, Otf *o, MathKernInfo *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "mathKernCoverageOffset", v->mathKernCoverageOffset);
	Bprint(f, "%*s%s: %ud\n", indent, "", "mathKernCount", v->mathKernCount);
	for(int i = 0; i < v->mathKernCount; i++){
		Bprint(f, "%*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;
	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(Biobuf *f, int indent, Otf *o, MathKern *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "heightCount", v->heightCount);
	for(int i = 0; i < v->heightCount; i++){
		Bprint(f, "%*s%s[%d]:\n", indent, "", "correctionHeight", i);
		print_MathValueRecord(f, indent+indentΔ, o, &v->correctionHeight[i]);
	}
	for(int i = 0; i < v->heightCount; i++){
		Bprint(f, "%*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;
	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(Biobuf *f, int indent, Otf *o, Coverage1 *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "glyphCount", v->glyphCount);
	for(int i = 0; i < v->glyphCount; i++)
		Bprint(f, "%*s%s[%d]: %ud\n", indent, "", "glyphArray", i, v->glyphArray[i]);
	USED(o);
}

int
read_RangeRecord(Otf *o, RangeRecord *v)
{
	u8int *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(Biobuf *f, int indent, Otf *o, RangeRecord *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "startGlyphID", v->startGlyphID);
	Bprint(f, "%*s%s: %ud\n", indent, "", "endGlyphID", v->endGlyphID);
	Bprint(f, "%*s%s: %ud\n", indent, "", "startCoverageIndex", v->startCoverageIndex);
	USED(o);
}

int
read_Coverage2(Otf *o, Coverage2 *v)
{
	u8int *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(Biobuf *f, int indent, Otf *o, Coverage2 *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "rangeCount", v->rangeCount);
	for(int i = 0; i < v->rangeCount; i++){
		Bprint(f, "%*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;
	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 (0x%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(Biobuf *f, int indent, Otf *o, Coverage *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "format", v->format);
	if(v->format == 1){
		Bprint(f, "%*s%s:\n", indent, "", "cov1");
		print_Coverage1(f, indent+indentΔ, o, &v->cov1);
	}
	if(v->format == 2){
		Bprint(f, "%*s%s:\n", indent, "", "cov2");
		print_Coverage2(f, indent+indentΔ, o, &v->cov2);
	}
	USED(o);
}

int
read_MathVariants(Otf *o, MathVariants *v)
{
	u8int *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;
	}
	return 0;
err:
	werrstr("%s: %r", "MathVariants");
	return -1;
}

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

int
read_MathGlyphInfo(Otf *o, MathGlyphInfo *v)
{
	u8int *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(Biobuf *f, int indent, Otf *o, MathGlyphInfo *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "mathItalicsCorrectionInfoOffset", v->mathItalicsCorrectionInfoOffset);
	Bprint(f, "%*s%s: %ud\n", indent, "", "mathTopAccentAttachmentOffset", v->mathTopAccentAttachmentOffset);
	Bprint(f, "%*s%s: %ud\n", indent, "", "extendedShapeCoverageOffset", v->extendedShapeCoverageOffset);
	Bprint(f, "%*s%s: %ud\n", indent, "", "mathKernInfoOffset", v->mathKernInfoOffset);
	Bprint(f, "%*s%s:\n", indent, "", "mathItalicsCorrectionInfo");
	if(v->mathItalicsCorrectionInfo != nil)
		print_MathItalicsCorrectionInfo(f, indent+indentΔ, o, v->mathItalicsCorrectionInfo);
	Bprint(f, "%*s%s:\n", indent, "", "mathTopAccentAttachment");
	if(v->mathTopAccentAttachment != nil)
		print_MathTopAccentAttachment(f, indent+indentΔ, o, v->mathTopAccentAttachment);
	Bprint(f, "%*s%s:\n", indent, "", "mathKernInfo");
	if(v->mathKernInfo != nil)
		print_MathKernInfo(f, indent+indentΔ, o, v->mathKernInfo);
	Bprint(f, "%*s%s:\n", indent, "", "extendedShapeCoverage");
	if(v->extendedShapeCoverage != nil)
		print_Coverage(f, indent+indentΔ, o, v->extendedShapeCoverage);
	USED(o);
}

int
read_MathGlyphVariantRecord(Otf *o, MathGlyphVariantRecord *v)
{
	u8int *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(Biobuf *f, int indent, Otf *o, MathGlyphVariantRecord *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "variantGlyph", v->variantGlyph);
	Bprint(f, "%*s%s: %ud\n", indent, "", "advanceMeasurement", v->advanceMeasurement);
	USED(o);
}

int
read_GlyphPart(Otf *o, GlyphPart *v)
{
	u8int *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(Biobuf *f, int indent, Otf *o, GlyphPart *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "glyphID", v->glyphID);
	Bprint(f, "%*s%s: %ud\n", indent, "", "startConnectorLength", v->startConnectorLength);
	Bprint(f, "%*s%s: %ud\n", indent, "", "endConnectorLength", v->endConnectorLength);
	Bprint(f, "%*s%s: %ud\n", indent, "", "fullAdvance", v->fullAdvance);
	Bprint(f, "%*s%s: %ud\n", indent, "", "partFlags", v->partFlags);
	USED(o);
}

int
read_GlyphAssembly(Otf *o, GlyphAssembly *v)
{
	u8int *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(Biobuf *f, int indent, Otf *o, GlyphAssembly *v)
{
	Bprint(f, "%*s%s:\n", indent, "", "italicsCorrection");
	print_MathValueRecord(f, indent+indentΔ, o, &v->italicsCorrection);
	Bprint(f, "%*s%s: %ud\n", indent, "", "partCount", v->partCount);
	for(int i = 0; i < v->partCount; i++){
		Bprint(f, "%*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;
	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(Biobuf *f, int indent, Otf *o, MathGlyphConstruction *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "glyphAssemblyOffset", v->glyphAssemblyOffset);
	Bprint(f, "%*s%s: %ud\n", indent, "", "variantCount", v->variantCount);
	for(int i = 0; i < v->variantCount; i++){
		Bprint(f, "%*s%s[%d]:\n", indent, "", "mathGlyphVariantRecords", i);
		print_MathGlyphVariantRecord(f, indent+indentΔ, o, &v->mathGlyphVariantRecords[i]);
	}
	Bprint(f, "%*s%s:\n", indent, "", "glyphAssembly");
	if(v->glyphAssembly != nil)
		print_GlyphAssembly(f, indent+indentΔ, o, v->glyphAssembly);
	USED(o);
}

int
read_TableMATH(Otf *o, TableMATH *v)
{
	u8int *b;
	if((b = otfreadn(o, 10)) == nil)
		goto err;
	u16int majorVersion = b[0]<<8 | b[1];
	if(majorVersion != 1){
		werrstr("%s: invalid value: %d (0x%ux)", "majorVersion", majorVersion, majorVersion);
		goto err;
	}
	u16int minorVersion = b[2]<<8 | b[3];
	if(minorVersion != 0){
		werrstr("%s: invalid value: %d (0x%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(Biobuf *f, int indent, Otf *o, TableMATH *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "mathConstantsOffset", v->mathConstantsOffset);
	Bprint(f, "%*s%s: %ud\n", indent, "", "mathGlyphInfoOffset", v->mathGlyphInfoOffset);
	Bprint(f, "%*s%s: %ud\n", indent, "", "mathVariantsOffset", v->mathVariantsOffset);
	Bprint(f, "%*s%s:\n", indent, "", "mathConstants");
	if(v->mathConstants != nil)
		print_MathConstants(f, indent+indentΔ, o, v->mathConstants);
	Bprint(f, "%*s%s:\n", indent, "", "mathGlyphInfo");
	if(v->mathGlyphInfo != nil)
		print_MathGlyphInfo(f, indent+indentΔ, o, v->mathGlyphInfo);
	Bprint(f, "%*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;
	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(Biobuf *f, int indent, Otf *o, KernPair *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "left", v->left);
	Bprint(f, "%*s%s: %ud\n", indent, "", "right", v->right);
	Bprint(f, "%*s%s: %d\n", indent, "", "value", v->value);
	USED(o);
}

int
read_KernSubtable0(Otf *o, KernSubtable0 *v)
{
	u8int *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(Biobuf *f, int indent, Otf *o, KernSubtable0 *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "nPairs", v->nPairs);
	Bprint(f, "%*s%s: %ud\n", indent, "", "searchRange", v->searchRange);
	Bprint(f, "%*s%s: %ud\n", indent, "", "entrySelector", v->entrySelector);
	Bprint(f, "%*s%s: %ud\n", indent, "", "rangeShift", v->rangeShift);
	for(int i = 0; i < v->nPairs; i++){
		Bprint(f, "%*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;
	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(Biobuf *f, int indent, Otf *o, KernClass *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "firstGlyph", v->firstGlyph);
	Bprint(f, "%*s%s: %ud\n", indent, "", "nGlyphs", v->nGlyphs);
	for(int i = 0; i < v->nGlyphs; i++)
		Bprint(f, "%*s%s[%d]: %ud\n", indent, "", "values", i, v->values[i]);
	USED(o);
}

int
read_KernSubtable2(Otf *o, KernSubtable2 *v)
{
	u8int *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(Biobuf *f, int indent, Otf *o, KernSubtable2 *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "rowWidth", v->rowWidth);
	Bprint(f, "%*s%s: %ud\n", indent, "", "leftClassOffset", v->leftClassOffset);
	Bprint(f, "%*s%s: %ud\n", indent, "", "rightClassOffset", v->rightClassOffset);
	Bprint(f, "%*s%s: %ud\n", indent, "", "kerningArrayOffset", v->kerningArrayOffset);
	Bprint(f, "%*s%s:\n", indent, "", "leftClass");
	if(v->leftClass != nil)
		print_KernClass(f, indent+indentΔ, o, v->leftClass);
	Bprint(f, "%*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++)
		Bprint(f, "%*s%s[%d]: %d\n", indent, "", "kerningArray", i, v->kerningArray[i]);
	USED(o);
}

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

void
print_KernSubtable(Biobuf *f, int indent, Otf *o, KernSubtable *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "length", v->length);
	Bprint(f, "%*s%s: %ud\n", indent, "", "coverage", v->coverage);
	USED(o);
}

int
read_TableKern(Otf *o, TableKern *v)
{
	u8int *b;
	if((b = otfreadn(o, 4)) == nil)
		goto err;
	u16int version = b[0]<<8 | b[1];
	if(version != 0){
		werrstr("%s: invalid value: %d (0x%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(Biobuf *f, int indent, Otf *o, TableKern *v)
{
	Bprint(f, "%*s%s: %ud\n", indent, "", "nTables", v->nTables);
	for(int i = 0; i < v->nTables; i++){
		Bprint(f, "%*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;
	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(Biobuf *f, int indent, Otf *o, TableLoca *v)
{
	if(o->indexToLocFormat == 0){
		for(int i = 0; i < (o->numGlyphs+1); i++)
			Bprint(f, "%*s%s[%d]: %ud\n", indent, "", "shortOffsets", i, v->shortOffsets[i]);
	}
	if(o->indexToLocFormat == 1){
		for(int i = 0; i < (o->numGlyphs+1); i++)
			Bprint(f, "%*s%s[%d]: %ud\n", indent, "", "longOffsets", i, v->longOffsets[i]);
	}
	USED(o);
}

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

int
read_TableRecord(Otf *o, TableRecord *v)
{
	u8int *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(Biobuf *f, int indent, Otf *o, TableRecord *v)
{
	Bprint(f, "%*s%s: %t\n", indent, "", "tableTag", v->tableTag);
	Bprint(f, "%*s%s: %ud\n", indent, "", "offset", v->offset);
	Bprint(f, "%*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;
	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 (0x%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);
					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);
					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);
					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);
					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);
					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);
					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);
					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);
					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);
					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);
					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);
					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);
					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);
					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);
					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){
						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);
					goto err;
				}
				rec->parsed = v->loca;
				rec->print = (void*)print_TableLoca;
				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);
					goto err;
				}
				rec->parsed = v->os∕2;
				rec->print = (void*)print_TableOS∕2;
				break;
			}
			if(otfpoprange(o) < 0)
				goto err;
		}
	}while(retry > 0);
	return 0;
err:
	werrstr("%s: %r", "TableDirectory");
	return -1;
}

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

int indentΔ = 2;

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

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

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

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