shithub: fnt

Download patch

ref: c8e83c81d910049123621d766bc837abc9103145
parent: dc095bf883919052348b9f8ba907bb3807e2283d
author: Sigrid Solveig Haflínudóttir <sigrid@ftrv.se>
date: Tue Jun 25 11:15:45 EDT 2024

move verbatim C code from gen.rkt to otf.[ch].in

--- a/gen.rkt
+++ b/gen.rkt
@@ -21,7 +21,7 @@
 
 (define/contract (indent lst)
   (-> (listof any/c) (listof string?))
-  (map (λ (str) (string-append "\t" str)) (flatten lst)))
+  (map (λ (str) (string-append "	" str)) (flatten lst)))
 
 (define/contract (c-typedef? s)
   (-> string? boolean?)
@@ -142,7 +142,7 @@
          [count
           #:when (type-string? t)
           (list (~a "if((b = otfreadn(o, " count ")) == nil)")
-                (~a "\tgoto err;")
+                (~a "	goto err;")
                 (if (equal? t String-UTF16)
                     (list (~a ref " = malloc(" count "/2+1);")
                           (~a "utf16to8((u8int*)" ref ", " count "/2+1, b, " count ");"))
@@ -153,7 +153,7 @@
               empty
               (list (if (number? count) empty (~a ref " = malloc(" count "*sizeof(*" ref "));"))
                     (~a "for(int i = 0; i < " count "; i++)")
-                    (~a "\t"
+                    (~a "	"
                         ref
                         "[i] = "
                         ((type-parse (field-type f)) b index (~a "i*" (size (field-type f))))
@@ -180,31 +180,30 @@
        (if (not at)
            lst
            (list (~a "if(" (fmt-expr at) " != 0){")
-                 (~a "\tif(otfpushrange(o, " (fmt-expr at) ", -1) < 0)")
-                 (~a "\t\tgoto err;")
+                 (~a "	if(otfpushrange(o, " (fmt-expr at) ", -1) < 0)")
+                 (~a "		goto err;")
                  (indent lst)
-                 (~a "\tif(otfpoprange(o) < 0)")
-                 (~a "\t\tgoto err;")
+                 (~a "	if(otfpoprange(o) < 0)")
+                 (~a "		goto err;")
                  (~a "}"))))
-     (at (list* (parse-if-error #t)
-                (if index
-                    empty
-                    (list (~a "\twerrstr(\"%s: %r\", \"" (field-name f) "\");")
-                          (~a "\tgoto err;")
-                          (~a "}")))
-                (match (test-cond (field-test f))
-                  [(list) empty]
-                  [(list a ...)
-                   (list (~a "if(" (string-join a " || ") "){")
-                         (~a "\twerrstr(\"%s: invalid value: %d (0x%ux)\", \""
-                             (field-name f)
-                             "\", "
-                             ref
-                             ", "
-                             ref
-                             ");")
-                         (~a "\tgoto err;")
-                         (~a "}"))]))))])
+     (at (list*
+          (parse-if-error #t)
+          (if index
+              empty
+              (list (~a "	werrstr(\"%s: %r\", \"" (field-name f) "\");") (~a "	goto err;") (~a "}")))
+          (match (test-cond (field-test f))
+            [(list) empty]
+            [(list a ...)
+             (list (~a "if(" (string-join a " || ") "){")
+                   (~a "	werrstr(\"%s: invalid value: %d (0x%ux)\", \""
+                       (field-name f)
+                       "\", "
+                       ref
+                       ", "
+                       ref
+                       ");")
+                   (~a "	goto err;")
+                   (~a "}"))]))))])
 
 (define/contract (field-attr f a)
   (-> field? symbol? any)
@@ -291,7 +290,7 @@
               ");")
           (list (if cnt empty (~a "Bprint(f, \"%*s%s:\\n\", indent, \"\", \"" (field-name f) "\");"))
                 (if is-ptr (~a "if(v->" (field-name f) " != nil)") empty)
-                (~a (if is-ptr "\t" "")
+                (~a (if is-ptr "	" "")
                     "print_"
                     (cmplx-name t)
                     "(f, indent+indentΔ, o, "
@@ -387,7 +386,7 @@
                    [lst (flatten (list (if (field-offset (car fields))
                                            empty
                                            (list (~a "if((b = otfreadn(o, " sum ")) == nil)")
-                                                 (~a "\tgoto err;")
+                                                 (~a "	goto err;")
                                                  (if unused "USED(b);" empty)))
                                        (parse-group fields)))])
               lst))))
@@ -397,13 +396,13 @@
              (~a "int")
              (~a "read_" (cmplx-name c) "(Otf *o, " (cmplx-name c) " *v)")
              (~a "{")
-             (~a "\tu8int *b;"))
+             (~a "	u8int *b;"))
        (indent (map gen-group-c (group-fields (cmplx-fields c))))
        (indent (filter-extra (cmplx-extra c) 'read))
-       (list (~a "\treturn 0;")
+       (list (~a "	return 0;")
              (~a "err:")
-             (~a "\twerrstr(\"%s: %r\", \"" (cmplx-name c) "\");")
-             (~a "\treturn -1;")
+             (~a "	werrstr(\"%s: %r\", \"" (cmplx-name c) "\");")
+             (~a "	return -1;")
              (~a "}"))
        (list (~a "")
              (~a "void")
@@ -411,7 +410,7 @@
              (~a "{")
              (indent (map field-print-c (cmplx-fields c)))
              (indent (filter-extra (cmplx-extra c) 'print))
-             (~a "\tUSED(o);")
+             (~a "	USED(o);")
              (~a "}")))))
    (define (c-type c)
      (cmplx-name c))])
@@ -561,80 +560,10 @@
 
 (out "otf.h"
      (λ ()
-       (printf #<<EOF
-/* this file is generated. do not modify. */
-typedef struct Otf Otf;
-#pragma incomplete Otf
+       (printf (port->string (open-input-file "otf.h.in") #:close? #t))
+       (printf "\n")
+       (printf (format gen-h))))
 
-typedef struct ComponentGlyph ComponentGlyph;
-
-enum {
-	CGLYPH_FL_WORD_ARGS = 1<<0,
-	CGLYPH_FL_SIGNED_XY = 1<<1,
-	CGLYPH_FL_ROUND_TO_GRID_XY = 1<<2,
-	CGLYPH_FL_SCALE = 1<<3,
-	CGLYPH_FL_MORE_COMPONENTS = 1<<5,
-	CGLYPH_FL_SCALE_XY = 1<<6,
-	CGLYPH_FL_2X2_TRANSFORM = 1<<7,
-	CGLYPH_FL_INSTRUCTIONS = 1<<8,
-	CGLYPH_FL_METRICS = 1<<9,
-	CGLYPH_FL_OVERLAP_COMPOUND = 1<<10,
-	CGLYPH_FL_SCALED_COMPONENT_OFFSET = 1<<11,
-	CGLYPH_FL_UNSCALED_COMPONENT_OFFSET = 1<<12,
-};
-
-struct ComponentGlyph {
-	u16int flags;
-	u16int glyphIndex;
-	union {
-		u8int u8;
-		s8int s8;
-		u16int u16;
-		s16int s16;
-	}arg1, arg2;
-	float scale, scaleX, scale01, scale10, scaleY;
-	u16int numInstr;
-	u8int *instr;
-	ComponentGlyph *next;
-};
-
-typedef struct Point Point;
-
-struct Point {
-	int x;
-	int y;
-	int onCurve;
-};
-
-typedef struct SimpleGlyph SimpleGlyph;
-
-struct SimpleGlyph {
-	u16int *endPtsOfContours;
-	u16int instructionLength;
-	u8int *instructions;
-
-	int numPoints;
-	Point *points;
-};
-
-EOF
-               )
-       (printf (format gen-h))
-       (printf #<<EOF
-
-extern int indentΔ;
-
-#pragma varargck type "T" s64int
-#pragma varargck type "t" u32int
-#pragma varargck type "V" u32int
-
-void otfinit(void);
-Otf *otfopen(char *path);
-void otfclose(Otf *o);
-
-EOF
-               )))
-
 (define (extra-context-fields c)
   (if (cmplx? c)
       (filter-map (λ (f) (and (field-context? f) (indent (gen-h f)))) (cmplx-fields c))
@@ -642,31 +571,8 @@
 
 (out "otf.c"
      (λ ()
-       (printf #<<EOF
-/* 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 */
-
-EOF
-               )
-       (printf (format extra-context-fields #:on-all remove-duplicates))
-       (printf #<<EOF
-};
-
-#include "otfpriv.h"
-
-EOF
-               )
+       (printf (string-replace (port->string (open-input-file "otf.c.in") #:close? #t)
+                               "OTF_EXTRA_FIELDS\n"
+                               (format extra-context-fields #:on-all remove-duplicates)))
+       (printf "\n")
        (printf (format (λ (c) (gen-c c #f #f))))))
--- a/mkfile
+++ b/mkfile
@@ -9,7 +9,6 @@
 
 HFILES=\
   otf.h\
-  otfpriv.h\
 
 default:V: all
 
--- a/otf.c
+++ b/otf.c
@@ -30,7 +30,453 @@
 	u16int axisValueCount;
 };
 
-#include "otfpriv.h"
+struct Range {
+	int start;
+	int len;
+	int prevoff;
+	Range *par;
+};
+
+#define rchr(s) (be ? ((s)[0]<<8 | (s)[1]) : ((s)[1]<<8 | (s)[0]))
+
+static u8int mark[] = {0x00, 0x00, 0xc0, 0xe0, 0xf0};
+
+static int
+utf16to8(u8int *o, int osz, u8int *s, int sz)
+{
+	u32int c, c2;
+	int i, wr, j, be;
+
+	i = 0;
+	be = 1;
+	if(s[0] == 0xfe && s[1] == 0xff)
+		i += 2;
+	else if(s[0] == 0xff && s[1] == 0xfe){
+		be = 0;
+		i += 2;
+	}
+
+	for(; i < sz-1 && osz > 1;){
+		c = rchr(&s[i]);
+		i += 2;
+		if(c >= 0xd800 && c <= 0xdbff && i < sz-1){
+			c2 = rchr(&s[i]);
+			if(c2 >= 0xdc00 && c2 <= 0xdfff){
+				c = 0x10000 | (c - 0xd800)<<10 | (c2 - 0xdc00);
+				i += 2;
+			}else
+				return -1;
+		}else if(c >= 0xdc00 && c <= 0xdfff)
+			return -1;
+
+		if(c < 0x80)
+			wr = 1;
+		else if(c < 0x800)
+			wr = 2;
+		else if(c < 0x10000)
+			wr = 3;
+		else
+			wr = 4;
+
+		osz -= wr;
+		if(osz < 1)
+			break;
+
+		o += wr;
+		for(j = wr; j > 1; j--){
+			*(--o) = (c & 0xbf) | 0x80;
+			c >>= 6;
+		}
+		*(--o) = (u8int)c | mark[wr];
+		o += wr;
+	}
+
+	*o = 0;
+	return i;
+}
+
+static char *
+strtoutf8(Otf *o, u8int *in, int sz)
+{
+	char *s;
+
+	// FIXME this is not sufficient, obviously - need more platform/encoding handling
+	if(o->platformID == 0 || (o->platformID == 3 && o->encodingID == 1)){
+		s = malloc(sz/2+1);
+		utf16to8((u8int*)s, sz/2+1, in, sz);
+	}else{
+		s = malloc(sz+1);
+		memcpy(s, in, sz);
+		s[sz] = 0;
+	}
+	return s;
+}
+
+Otf *
+otfopen(char *path)
+{
+	Otf *o;
+	Biobuf *f;
+
+	if((f = Bopen(path, OREAD)) == nil)
+		return nil;
+	if((o = calloc(1, sizeof(*o))) == nil){
+		werrstr("no memory");
+		Bterm(f);
+	}else{
+		o->f = f;
+	}
+	return o;
+}
+
+void
+otfclose(Otf *o)
+{
+	if(o == nil)
+		return;
+	// FIXME traverse and free everything
+	free(o);
+}
+
+static int
+otfpushrange(Otf *o, int off, int len)
+{
+	Range *r;
+	int x;
+
+	r = nil;
+	if(o->r != nil){
+		if(len < 0)
+			len = o->r->len - off;
+		if(len < 0 || off+len > o->r->len){
+			werrstr("range overflow (len %d) by %d bytes", len, off+len - o->r->len);
+			goto err;
+		}
+		off += o->r->start;
+	}else if(len < 0){
+		len = 0x7fffffff;
+	}
+	if((r = malloc(sizeof(*r))) == nil){
+		werrstr("no memory");
+		goto err;
+	}
+	r->par = o->r;
+	r->start = off;
+	r->len = len;
+	r->prevoff = o->off;
+	if((x = Bseek(o->f, off, 0)) != off){
+		werrstr("seek offset: need %d, got %d", off, x);
+		goto err;
+	}
+	o->off = off;
+	o->r = r;
+	return 0;
+err:
+	free(r);
+	return -1;
+}
+
+static int
+otfpoprange(Otf *o)
+{
+	Range *r;
+	int x;
+
+	r = o->r;
+	if(r == nil){
+		werrstr("pop without push");
+		goto err;
+	}
+	if((x = Bseek(o->f, r->prevoff, 0)) != r->prevoff){
+		werrstr("seek offset: need %d, got %d", r->prevoff, x);
+		goto err;
+	}
+	o->off = r->prevoff;
+	o->r = r->par;
+	free(r);
+	return 0;
+err:
+	return -1;
+}
+
+static u8int *
+otfreadn(Otf *o, int n)
+{
+	Range *r;
+	u8int *b;
+	int x;
+
+	r = o->r;
+	if(r != nil && o->off+n > r->start+r->len){
+		werrstr("need %d at %d, have %d at %d", n, o->off, r->len, r->start);
+		goto err;
+	}
+	if(n > o->bufsz){
+		if((b = realloc(o->buf, n)) == nil){
+			werrstr("no memory");
+			goto err;
+		}
+		o->buf = b;
+		o->bufsz = n;
+	}
+	if((x = Bread(o->f, o->buf, n)) != n){
+		werrstr("need %d, got %d; off %d", n, x, o->off);
+		goto err;
+	}
+	o->off += n;
+
+	return o->buf;
+err:
+	return nil;
+}
+
+static int
+otfarray(Otf *o, void **arr_, void *fun_, int elsz, int num)
+{
+	int i;
+	int (*fun)(Otf*, void*);
+	u8int *arr;
+
+	if((arr = calloc(num, elsz)) == nil){
+		werrstr("no memory");
+		goto err;
+	}
+	fun = fun_;
+	for(i = 0; i < num; i++){
+		if(fun(o, arr + i*elsz) < 0)
+			goto err;
+	}
+	*arr_ = arr;
+	return 0;
+err:
+	free(arr);
+	return -1;
+}
+
+static int
+read_ComponentGlyph(Otf *o, ComponentGlyph **out, int instr)
+{
+	ComponentGlyph *v;
+	u8int *b;
+
+	if((v = calloc(1, sizeof(*v))) == nil){
+nomem:
+		werrstr("no memory");
+		goto err;
+	}
+
+	if((b = otfreadn(o, 2*2)) == nil)
+		goto err;
+	v->flags = b[0]<<8 | b[1];
+	v->glyphIndex = b[2]<<8 | b[3];
+	instr |= v->flags & CGLYPH_FL_INSTRUCTIONS;
+
+	if((v->flags & CGLYPH_FL_WORD_ARGS) != 0 && (b = otfreadn(o, 2*2)) != nil){
+		v->arg1.u16 = b[0]<<8 | b[1];
+		v->arg2.u16 = b[2]<<8 | b[3];
+	}else if((b = otfreadn(o, 2)) != nil){
+		v->arg1.u8 = b[0];
+		v->arg2.u8 = b[1];
+	}
+	if(b == nil)
+		goto err;
+	if((v->flags & CGLYPH_FL_SCALE) != 0 && (b = otfreadn(o, 2)) != nil){
+		v->scale = (b[0]<<8 | b[1]>>14)+(b[0]<<8 | b[1]&((1<<14)-1))/16384.0;
+	}else if((v->flags & CGLYPH_FL_SCALE_XY) != 0 && (b = otfreadn(o, 2*2)) != nil){
+		v->scaleX = (b[0]<<8 | b[1]>>14)+(b[0]<<8 | b[1]&((1<<14)-1))/16384.0;
+		v->scaleY = (b[2]<<8 | b[3]>>14)+(b[2]<<8 | b[3]&((1<<14)-1))/16384.0;
+	}else if((v->flags & CGLYPH_FL_2X2_TRANSFORM) != 0 && (b = otfreadn(o, 2*2*2)) != nil){
+		v->scaleX = (b[0]<<8 | b[1]>>14)+(b[0]<<8 | b[1]&((1<<14)-1))/16384.0;
+		v->scale01 = (b[2]<<8 | b[3]>>14)+(b[2]<<8 | b[3]&((1<<14)-1))/16384.0;
+		v->scale10 = (b[4]<<8 | b[5]>>14)+(b[4]<<8 | b[5]&((1<<14)-1))/16384.0;
+		v->scaleY = (b[6]<<8 | b[7]>>14)+(b[6]<<8 | b[7]&((1<<14)-1))/16384.0;
+	}
+	if(b == nil)
+		goto err;
+	if((v->flags & CGLYPH_FL_MORE_COMPONENTS) != 0){
+		if(read_ComponentGlyph(o, &v->next, instr) != 0){
+			free(v);
+			return -1;
+		}
+	}else if(instr != 0){
+		if((b = otfreadn(o, 2)) == nil)
+			goto err;
+		v->numInstr = b[0]<<8 | b[1];
+		if((v->instr = malloc(v->numInstr)) == nil)
+			goto nomem;
+		if(v->numInstr > 0 && (b = otfreadn(o, v->numInstr)) == nil)
+			goto err;
+		memcpy(v->instr, b, v->numInstr);
+	}
+	*out = v;
+	return 0;
+err:
+	if(v != nil)
+		free(v->instr);
+	free(v);
+	werrstr("ComponentGlyph: %r");
+	return -1;
+}
+
+enum {
+	GLYPH_FL_ON_CURVE_POINT = 1<<0,
+	GLYPH_FL_X_SHORT_VECTOR = 1<<1,
+	GLYPH_FL_Y_SHORT_VECTOR = 1<<2,
+	GLYPH_FL_REPEAT = 1<<3,
+	GLYPH_FL_X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR = 1<<4,
+	GLYPH_FL_Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR = 1<<5,
+	GLYPH_FL_OVERLAP_SIMPLE = 1<<6,
+};
+
+static int
+read_SimpleGlyph(Otf *o, SimpleGlyph **out)
+{
+	SimpleGlyph *v;
+	u8int *b, *flags;
+	int i, n, nflags, c;
+
+	flags = nil;
+	if((v = calloc(1, sizeof(*v))) == nil){
+nomem:
+		werrstr("no memory");
+		goto err;
+	}
+
+	n = o->numberOfContours;
+	if((b = otfreadn(o, 2*n+2)) == nil)
+		goto err;
+	if((v->endPtsOfContours = malloc(2*n)) == nil)
+		goto nomem;
+	for(int i = 0; i < n; i++)
+		v->endPtsOfContours[i] = b[2*i+0]<<8 | b[2*i+1];
+
+	v->instructionLength = b[2*n+0]<<8 | b[2*n+1];
+	n = v->instructionLength;
+	if((b = otfreadn(o, n)) == nil)
+		goto err;
+	if((v->instructions = malloc(n)) == nil)
+		goto nomem;
+	memcpy(v->instructions, b, n);
+
+	if((flags = malloc(v->numPoints)) == nil) /* not supposed to be more than that, 64Kb max */
+		goto nomem;
+	for(nflags = 0; nflags < v->numPoints;){
+		if((b = otfreadn(o, 1)) == nil)
+			goto err;
+		flags[nflags] = *b;
+		if((flags[nflags] & GLYPH_FL_REPEAT) != 0){
+			flags[nflags] ^= GLYPH_FL_REPEAT;
+			if((b = otfreadn(o, 1)) == nil)
+				goto err;
+			if(nflags + 1 + *b > v->numPoints){
+				werrstr("repeat overflow");
+				goto err;
+			}
+			memset(flags+nflags+1, flags[nflags], *b);
+			nflags += 1 + *b;
+		}else{
+			nflags++;
+		}
+	}
+
+	v->numPoints = v->endPtsOfContours[o->numberOfContours-1];
+	if((v->points = calloc(v->numPoints, sizeof(*v->points))) == nil)
+		goto nomem;
+
+	/* flags first */
+	for(i = n = 0; i < nflags && n < v->numPoints; i++, n++)
+		v->points[n].onCurve = (flags[i] & GLYPH_FL_ON_CURVE_POINT) != 0;
+
+	/* read packed x coordinates */
+	c = 0;
+	for(i = n = 0; i < nflags && n < v->numPoints; i++){
+		if((flags[i] & GLYPH_FL_X_SHORT_VECTOR) != 0){
+			if((b = otfreadn(o, 1)) == nil)
+				goto err;
+			c = *b;
+			if((flags[i] & GLYPH_FL_X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR) == 0)
+				c = -c;
+			v->points[n++].x = c;
+		}else if((flags[i] & GLYPH_FL_X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR) != 0){
+			v->points[n++].x = c;
+		}else{
+			if((b = otfreadn(o, 2)) == nil)
+				goto err;
+			c += (s16int)(b[0]<<8 | b[1]);
+			v->points[n++].x = c;
+		}
+	}
+
+	/* read packed y coordinates */
+	c = 0;
+	for(i = n = 0; i < nflags && n < v->numPoints; i++){
+		if((flags[i] & GLYPH_FL_Y_SHORT_VECTOR) != 0){
+			if((b = otfreadn(o, 1)) == nil)
+				goto err;
+			c = *b;
+			if((flags[i] & GLYPH_FL_Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR) == 0)
+				c = -c;
+			v->points[n++].y = c;
+		}else if((flags[i] & GLYPH_FL_Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR) != 0){
+			v->points[n++].y = c;
+		}else{
+			if((b = otfreadn(o, 2)) == nil)
+				goto err;
+			c += (s16int)(b[0]<<8 | b[1]);
+			v->points[n++].y = c;
+		}
+	}
+
+	free(flags);
+	*out = v;
+	return 0;
+err:
+	free(flags);
+	if(v != nil){
+		free(v->endPtsOfContours);
+		free(v->instructions);
+		free(v->points);
+	}
+	free(v);
+	werrstr("SimpleGlyph: %r");
+	return -1;
+}
+
+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);
+	if(v == 0)
+		return fmtprint(f, "<nil>");
+	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);
+}
+
 
 int
 read_SubHeader(Otf *o, SubHeader *v)
--- /dev/null
+++ b/otf.c.in
@@ -1,0 +1,464 @@
+/* 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 */
+OTF_EXTRA_FIELDS
+};
+
+struct Range {
+	int start;
+	int len;
+	int prevoff;
+	Range *par;
+};
+
+#define rchr(s) (be ? ((s)[0]<<8 | (s)[1]) : ((s)[1]<<8 | (s)[0]))
+
+static u8int mark[] = {0x00, 0x00, 0xc0, 0xe0, 0xf0};
+
+static int
+utf16to8(u8int *o, int osz, u8int *s, int sz)
+{
+	u32int c, c2;
+	int i, wr, j, be;
+
+	i = 0;
+	be = 1;
+	if(s[0] == 0xfe && s[1] == 0xff)
+		i += 2;
+	else if(s[0] == 0xff && s[1] == 0xfe){
+		be = 0;
+		i += 2;
+	}
+
+	for(; i < sz-1 && osz > 1;){
+		c = rchr(&s[i]);
+		i += 2;
+		if(c >= 0xd800 && c <= 0xdbff && i < sz-1){
+			c2 = rchr(&s[i]);
+			if(c2 >= 0xdc00 && c2 <= 0xdfff){
+				c = 0x10000 | (c - 0xd800)<<10 | (c2 - 0xdc00);
+				i += 2;
+			}else
+				return -1;
+		}else if(c >= 0xdc00 && c <= 0xdfff)
+			return -1;
+
+		if(c < 0x80)
+			wr = 1;
+		else if(c < 0x800)
+			wr = 2;
+		else if(c < 0x10000)
+			wr = 3;
+		else
+			wr = 4;
+
+		osz -= wr;
+		if(osz < 1)
+			break;
+
+		o += wr;
+		for(j = wr; j > 1; j--){
+			*(--o) = (c & 0xbf) | 0x80;
+			c >>= 6;
+		}
+		*(--o) = (u8int)c | mark[wr];
+		o += wr;
+	}
+
+	*o = 0;
+	return i;
+}
+
+static char *
+strtoutf8(Otf *o, u8int *in, int sz)
+{
+	char *s;
+
+	// FIXME this is not sufficient, obviously - need more platform/encoding handling
+	if(o->platformID == 0 || (o->platformID == 3 && o->encodingID == 1)){
+		s = malloc(sz/2+1);
+		utf16to8((u8int*)s, sz/2+1, in, sz);
+	}else{
+		s = malloc(sz+1);
+		memcpy(s, in, sz);
+		s[sz] = 0;
+	}
+	return s;
+}
+
+Otf *
+otfopen(char *path)
+{
+	Otf *o;
+	Biobuf *f;
+
+	if((f = Bopen(path, OREAD)) == nil)
+		return nil;
+	if((o = calloc(1, sizeof(*o))) == nil){
+		werrstr("no memory");
+		Bterm(f);
+	}else{
+		o->f = f;
+	}
+	return o;
+}
+
+void
+otfclose(Otf *o)
+{
+	if(o == nil)
+		return;
+	// FIXME traverse and free everything
+	free(o);
+}
+
+static int
+otfpushrange(Otf *o, int off, int len)
+{
+	Range *r;
+	int x;
+
+	r = nil;
+	if(o->r != nil){
+		if(len < 0)
+			len = o->r->len - off;
+		if(len < 0 || off+len > o->r->len){
+			werrstr("range overflow (len %d) by %d bytes", len, off+len - o->r->len);
+			goto err;
+		}
+		off += o->r->start;
+	}else if(len < 0){
+		len = 0x7fffffff;
+	}
+	if((r = malloc(sizeof(*r))) == nil){
+		werrstr("no memory");
+		goto err;
+	}
+	r->par = o->r;
+	r->start = off;
+	r->len = len;
+	r->prevoff = o->off;
+	if((x = Bseek(o->f, off, 0)) != off){
+		werrstr("seek offset: need %d, got %d", off, x);
+		goto err;
+	}
+	o->off = off;
+	o->r = r;
+	return 0;
+err:
+	free(r);
+	return -1;
+}
+
+static int
+otfpoprange(Otf *o)
+{
+	Range *r;
+	int x;
+
+	r = o->r;
+	if(r == nil){
+		werrstr("pop without push");
+		goto err;
+	}
+	if((x = Bseek(o->f, r->prevoff, 0)) != r->prevoff){
+		werrstr("seek offset: need %d, got %d", r->prevoff, x);
+		goto err;
+	}
+	o->off = r->prevoff;
+	o->r = r->par;
+	free(r);
+	return 0;
+err:
+	return -1;
+}
+
+static u8int *
+otfreadn(Otf *o, int n)
+{
+	Range *r;
+	u8int *b;
+	int x;
+
+	r = o->r;
+	if(r != nil && o->off+n > r->start+r->len){
+		werrstr("need %d at %d, have %d at %d", n, o->off, r->len, r->start);
+		goto err;
+	}
+	if(n > o->bufsz){
+		if((b = realloc(o->buf, n)) == nil){
+			werrstr("no memory");
+			goto err;
+		}
+		o->buf = b;
+		o->bufsz = n;
+	}
+	if((x = Bread(o->f, o->buf, n)) != n){
+		werrstr("need %d, got %d; off %d", n, x, o->off);
+		goto err;
+	}
+	o->off += n;
+
+	return o->buf;
+err:
+	return nil;
+}
+
+static int
+otfarray(Otf *o, void **arr_, void *fun_, int elsz, int num)
+{
+	int i;
+	int (*fun)(Otf*, void*);
+	u8int *arr;
+
+	if((arr = calloc(num, elsz)) == nil){
+		werrstr("no memory");
+		goto err;
+	}
+	fun = fun_;
+	for(i = 0; i < num; i++){
+		if(fun(o, arr + i*elsz) < 0)
+			goto err;
+	}
+	*arr_ = arr;
+	return 0;
+err:
+	free(arr);
+	return -1;
+}
+
+static int
+read_ComponentGlyph(Otf *o, ComponentGlyph **out, int instr)
+{
+	ComponentGlyph *v;
+	u8int *b;
+
+	if((v = calloc(1, sizeof(*v))) == nil){
+nomem:
+		werrstr("no memory");
+		goto err;
+	}
+
+	if((b = otfreadn(o, 2*2)) == nil)
+		goto err;
+	v->flags = b[0]<<8 | b[1];
+	v->glyphIndex = b[2]<<8 | b[3];
+	instr |= v->flags & CGLYPH_FL_INSTRUCTIONS;
+
+	if((v->flags & CGLYPH_FL_WORD_ARGS) != 0 && (b = otfreadn(o, 2*2)) != nil){
+		v->arg1.u16 = b[0]<<8 | b[1];
+		v->arg2.u16 = b[2]<<8 | b[3];
+	}else if((b = otfreadn(o, 2)) != nil){
+		v->arg1.u8 = b[0];
+		v->arg2.u8 = b[1];
+	}
+	if(b == nil)
+		goto err;
+	if((v->flags & CGLYPH_FL_SCALE) != 0 && (b = otfreadn(o, 2)) != nil){
+		v->scale = (b[0]<<8 | b[1]>>14)+(b[0]<<8 | b[1]&((1<<14)-1))/16384.0;
+	}else if((v->flags & CGLYPH_FL_SCALE_XY) != 0 && (b = otfreadn(o, 2*2)) != nil){
+		v->scaleX = (b[0]<<8 | b[1]>>14)+(b[0]<<8 | b[1]&((1<<14)-1))/16384.0;
+		v->scaleY = (b[2]<<8 | b[3]>>14)+(b[2]<<8 | b[3]&((1<<14)-1))/16384.0;
+	}else if((v->flags & CGLYPH_FL_2X2_TRANSFORM) != 0 && (b = otfreadn(o, 2*2*2)) != nil){
+		v->scaleX = (b[0]<<8 | b[1]>>14)+(b[0]<<8 | b[1]&((1<<14)-1))/16384.0;
+		v->scale01 = (b[2]<<8 | b[3]>>14)+(b[2]<<8 | b[3]&((1<<14)-1))/16384.0;
+		v->scale10 = (b[4]<<8 | b[5]>>14)+(b[4]<<8 | b[5]&((1<<14)-1))/16384.0;
+		v->scaleY = (b[6]<<8 | b[7]>>14)+(b[6]<<8 | b[7]&((1<<14)-1))/16384.0;
+	}
+	if(b == nil)
+		goto err;
+	if((v->flags & CGLYPH_FL_MORE_COMPONENTS) != 0){
+		if(read_ComponentGlyph(o, &v->next, instr) != 0){
+			free(v);
+			return -1;
+		}
+	}else if(instr != 0){
+		if((b = otfreadn(o, 2)) == nil)
+			goto err;
+		v->numInstr = b[0]<<8 | b[1];
+		if((v->instr = malloc(v->numInstr)) == nil)
+			goto nomem;
+		if(v->numInstr > 0 && (b = otfreadn(o, v->numInstr)) == nil)
+			goto err;
+		memcpy(v->instr, b, v->numInstr);
+	}
+	*out = v;
+	return 0;
+err:
+	if(v != nil)
+		free(v->instr);
+	free(v);
+	werrstr("ComponentGlyph: %r");
+	return -1;
+}
+
+enum {
+	GLYPH_FL_ON_CURVE_POINT = 1<<0,
+	GLYPH_FL_X_SHORT_VECTOR = 1<<1,
+	GLYPH_FL_Y_SHORT_VECTOR = 1<<2,
+	GLYPH_FL_REPEAT = 1<<3,
+	GLYPH_FL_X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR = 1<<4,
+	GLYPH_FL_Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR = 1<<5,
+	GLYPH_FL_OVERLAP_SIMPLE = 1<<6,
+};
+
+static int
+read_SimpleGlyph(Otf *o, SimpleGlyph **out)
+{
+	SimpleGlyph *v;
+	u8int *b, *flags;
+	int i, n, nflags, c;
+
+	flags = nil;
+	if((v = calloc(1, sizeof(*v))) == nil){
+nomem:
+		werrstr("no memory");
+		goto err;
+	}
+
+	n = o->numberOfContours;
+	if((b = otfreadn(o, 2*n+2)) == nil)
+		goto err;
+	if((v->endPtsOfContours = malloc(2*n)) == nil)
+		goto nomem;
+	for(int i = 0; i < n; i++)
+		v->endPtsOfContours[i] = b[2*i+0]<<8 | b[2*i+1];
+
+	v->instructionLength = b[2*n+0]<<8 | b[2*n+1];
+	n = v->instructionLength;
+	if((b = otfreadn(o, n)) == nil)
+		goto err;
+	if((v->instructions = malloc(n)) == nil)
+		goto nomem;
+	memcpy(v->instructions, b, n);
+
+	if((flags = malloc(v->numPoints)) == nil) /* not supposed to be more than that, 64Kb max */
+		goto nomem;
+	for(nflags = 0; nflags < v->numPoints;){
+		if((b = otfreadn(o, 1)) == nil)
+			goto err;
+		flags[nflags] = *b;
+		if((flags[nflags] & GLYPH_FL_REPEAT) != 0){
+			flags[nflags] ^= GLYPH_FL_REPEAT;
+			if((b = otfreadn(o, 1)) == nil)
+				goto err;
+			if(nflags + 1 + *b > v->numPoints){
+				werrstr("repeat overflow");
+				goto err;
+			}
+			memset(flags+nflags+1, flags[nflags], *b);
+			nflags += 1 + *b;
+		}else{
+			nflags++;
+		}
+	}
+
+	v->numPoints = v->endPtsOfContours[o->numberOfContours-1];
+	if((v->points = calloc(v->numPoints, sizeof(*v->points))) == nil)
+		goto nomem;
+
+	/* flags first */
+	for(i = n = 0; i < nflags && n < v->numPoints; i++, n++)
+		v->points[n].onCurve = (flags[i] & GLYPH_FL_ON_CURVE_POINT) != 0;
+
+	/* read packed x coordinates */
+	c = 0;
+	for(i = n = 0; i < nflags && n < v->numPoints; i++){
+		if((flags[i] & GLYPH_FL_X_SHORT_VECTOR) != 0){
+			if((b = otfreadn(o, 1)) == nil)
+				goto err;
+			c = *b;
+			if((flags[i] & GLYPH_FL_X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR) == 0)
+				c = -c;
+			v->points[n++].x = c;
+		}else if((flags[i] & GLYPH_FL_X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR) != 0){
+			v->points[n++].x = c;
+		}else{
+			if((b = otfreadn(o, 2)) == nil)
+				goto err;
+			c += (s16int)(b[0]<<8 | b[1]);
+			v->points[n++].x = c;
+		}
+	}
+
+	/* read packed y coordinates */
+	c = 0;
+	for(i = n = 0; i < nflags && n < v->numPoints; i++){
+		if((flags[i] & GLYPH_FL_Y_SHORT_VECTOR) != 0){
+			if((b = otfreadn(o, 1)) == nil)
+				goto err;
+			c = *b;
+			if((flags[i] & GLYPH_FL_Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR) == 0)
+				c = -c;
+			v->points[n++].y = c;
+		}else if((flags[i] & GLYPH_FL_Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR) != 0){
+			v->points[n++].y = c;
+		}else{
+			if((b = otfreadn(o, 2)) == nil)
+				goto err;
+			c += (s16int)(b[0]<<8 | b[1]);
+			v->points[n++].y = c;
+		}
+	}
+
+	free(flags);
+	*out = v;
+	return 0;
+err:
+	free(flags);
+	if(v != nil){
+		free(v->endPtsOfContours);
+		free(v->instructions);
+		free(v->points);
+	}
+	free(v);
+	werrstr("SimpleGlyph: %r");
+	return -1;
+}
+
+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);
+	if(v == 0)
+		return fmtprint(f, "<nil>");
+	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);
+}
--- a/otf.h
+++ b/otf.h
@@ -52,6 +52,17 @@
 	int numPoints;
 	Point *points;
 };
+
+extern int indentΔ;
+
+#pragma varargck type "T" s64int
+#pragma varargck type "t" u32int
+#pragma varargck type "V" u32int
+
+void otfinit(void);
+Otf *otfopen(char *path);
+void otfclose(Otf *o);
+
 typedef struct SubHeader SubHeader;
 typedef struct MapGroup MapGroup;
 typedef struct SubtableCmap0 SubtableCmap0;
@@ -1559,13 +1570,3 @@
 
 int read_TableDirectory(Otf *o, TableDirectory *v);
 void print_TableDirectory(Biobuf *f, int indent, Otf *o, TableDirectory *v);
-
-extern int indentΔ;
-
-#pragma varargck type "T" s64int
-#pragma varargck type "t" u32int
-#pragma varargck type "V" u32int
-
-void otfinit(void);
-Otf *otfopen(char *path);
-void otfclose(Otf *o);
--- /dev/null
+++ b/otf.h.in
@@ -1,0 +1,64 @@
+/* this file is generated. do not modify. */
+typedef struct Otf Otf;
+#pragma incomplete Otf
+
+typedef struct ComponentGlyph ComponentGlyph;
+
+enum {
+	CGLYPH_FL_WORD_ARGS = 1<<0,
+	CGLYPH_FL_SIGNED_XY = 1<<1,
+	CGLYPH_FL_ROUND_TO_GRID_XY = 1<<2,
+	CGLYPH_FL_SCALE = 1<<3,
+	CGLYPH_FL_MORE_COMPONENTS = 1<<5,
+	CGLYPH_FL_SCALE_XY = 1<<6,
+	CGLYPH_FL_2X2_TRANSFORM = 1<<7,
+	CGLYPH_FL_INSTRUCTIONS = 1<<8,
+	CGLYPH_FL_METRICS = 1<<9,
+	CGLYPH_FL_OVERLAP_COMPOUND = 1<<10,
+	CGLYPH_FL_SCALED_COMPONENT_OFFSET = 1<<11,
+	CGLYPH_FL_UNSCALED_COMPONENT_OFFSET = 1<<12,
+};
+
+struct ComponentGlyph {
+	u16int flags;
+	u16int glyphIndex;
+	union {
+		u8int u8;
+		s8int s8;
+		u16int u16;
+		s16int s16;
+	}arg1, arg2;
+	float scale, scaleX, scale01, scale10, scaleY;
+	u16int numInstr;
+	u8int *instr;
+	ComponentGlyph *next;
+};
+
+typedef struct Point Point;
+
+struct Point {
+	int x;
+	int y;
+	int onCurve;
+};
+
+typedef struct SimpleGlyph SimpleGlyph;
+
+struct SimpleGlyph {
+	u16int *endPtsOfContours;
+	u16int instructionLength;
+	u8int *instructions;
+
+	int numPoints;
+	Point *points;
+};
+
+extern int indentΔ;
+
+#pragma varargck type "T" s64int
+#pragma varargck type "t" u32int
+#pragma varargck type "V" u32int
+
+void otfinit(void);
+Otf *otfopen(char *path);
+void otfclose(Otf *o);
--- a/otf.rkt
+++ b/otf.rkt
@@ -178,7 +178,7 @@
          {int16 yMax}
          {uint16 macStyle}
          {uint16 lowestRecPPEM}
-         {int16 fontDirectionHint unused (>= -2) (<= 2)}
+         {int16 fontDirectionHint (>= -2) (<= 2) unused}
          {int16 indexToLocFormat ->o (<= 1)}
          {int16 glyphDataFormat unused (== 0)}
          #:tag "head")
@@ -212,9 +212,9 @@
  (list (cons 'field (list (~a "ComponentGlyph *component;") (~a "SimpleGlyph *simple;")))
        (cons 'read
              (list (~a "if(v->numberOfContours < 0 && read_ComponentGlyph(o, &v->component, 0) < 0)")
-                   (~a "\tgoto err;")
+                   (~a "	goto err;")
                    (~a "if(v->numberOfContours > 0 && read_SimpleGlyph(o, &v->simple) < 0)")
-                   (~a "\tgoto err;")))))
+                   (~a "	goto err;")))))
 
 (mkcmplx LongHorMetric {UFWORD advanceWidth} {FWORD lsb})
 
--- a/otfpriv.h
+++ /dev/null
@@ -1,446 +1,0 @@
-struct Range {
-	int start;
-	int len;
-	int prevoff;
-	Range *par;
-};
-
-#define rchr(s) (be ? ((s)[0]<<8 | (s)[1]) : ((s)[1]<<8 | (s)[0]))
-
-static u8int mark[] = {0x00, 0x00, 0xc0, 0xe0, 0xf0};
-
-static int
-utf16to8(u8int *o, int osz, u8int *s, int sz)
-{
-	u32int c, c2;
-	int i, wr, j, be;
-
-	i = 0;
-	be = 1;
-	if(s[0] == 0xfe && s[1] == 0xff)
-		i += 2;
-	else if(s[0] == 0xff && s[1] == 0xfe){
-		be = 0;
-		i += 2;
-	}
-
-	for(; i < sz-1 && osz > 1;){
-		c = rchr(&s[i]);
-		i += 2;
-		if(c >= 0xd800 && c <= 0xdbff && i < sz-1){
-			c2 = rchr(&s[i]);
-			if(c2 >= 0xdc00 && c2 <= 0xdfff){
-				c = 0x10000 | (c - 0xd800)<<10 | (c2 - 0xdc00);
-				i += 2;
-			}else
-				return -1;
-		}else if(c >= 0xdc00 && c <= 0xdfff)
-			return -1;
-
-		if(c < 0x80)
-			wr = 1;
-		else if(c < 0x800)
-			wr = 2;
-		else if(c < 0x10000)
-			wr = 3;
-		else
-			wr = 4;
-
-		osz -= wr;
-		if(osz < 1)
-			break;
-
-		o += wr;
-		for(j = wr; j > 1; j--){
-			*(--o) = (c & 0xbf) | 0x80;
-			c >>= 6;
-		}
-		*(--o) = (u8int)c | mark[wr];
-		o += wr;
-	}
-
-	*o = 0;
-	return i;
-}
-
-static char *
-strtoutf8(Otf *o, u8int *in, int sz)
-{
-	char *s;
-
-	// FIXME this is not sufficient, obviously - need more platform/encoding handling
-	if(o->platformID == 0 || (o->platformID == 3 && o->encodingID == 1)){
-		s = malloc(sz/2+1);
-		utf16to8((u8int*)s, sz/2+1, in, sz);
-	}else{
-		s = malloc(sz+1);
-		memcpy(s, in, sz);
-		s[sz] = 0;
-	}
-	return s;
-}
-
-Otf *
-otfopen(char *path)
-{
-	Otf *o;
-	Biobuf *f;
-
-	if((f = Bopen(path, OREAD)) == nil)
-		return nil;
-	if((o = calloc(1, sizeof(*o))) == nil){
-		werrstr("no memory");
-		Bterm(f);
-	}else{
-		o->f = f;
-	}
-	return o;
-}
-
-void
-otfclose(Otf *o)
-{
-	if(o == nil)
-		return;
-	// FIXME traverse and free everything
-	free(o);
-}
-
-static int
-otfpushrange(Otf *o, int off, int len)
-{
-	Range *r;
-	int x;
-
-	r = nil;
-	if(o->r != nil){
-		if(len < 0)
-			len = o->r->len - off;
-		if(len < 0 || off+len > o->r->len){
-			werrstr("range overflow (len %d) by %d bytes", len, off+len - o->r->len);
-			goto err;
-		}
-		off += o->r->start;
-	}else if(len < 0){
-		len = 0x7fffffff;
-	}
-	if((r = malloc(sizeof(*r))) == nil){
-		werrstr("no memory");
-		goto err;
-	}
-	r->par = o->r;
-	r->start = off;
-	r->len = len;
-	r->prevoff = o->off;
-	if((x = Bseek(o->f, off, 0)) != off){
-		werrstr("seek offset: need %d, got %d", off, x);
-		goto err;
-	}
-	o->off = off;
-	o->r = r;
-	return 0;
-err:
-	free(r);
-	return -1;
-}
-
-static int
-otfpoprange(Otf *o)
-{
-	Range *r;
-	int x;
-
-	r = o->r;
-	if(r == nil){
-		werrstr("pop without push");
-		goto err;
-	}
-	if((x = Bseek(o->f, r->prevoff, 0)) != r->prevoff){
-		werrstr("seek offset: need %d, got %d", r->prevoff, x);
-		goto err;
-	}
-	o->off = r->prevoff;
-	o->r = r->par;
-	free(r);
-	return 0;
-err:
-	return -1;
-}
-
-static u8int *
-otfreadn(Otf *o, int n)
-{
-	Range *r;
-	u8int *b;
-	int x;
-
-	r = o->r;
-	if(r != nil && o->off+n > r->start+r->len){
-		werrstr("need %d at %d, have %d at %d", n, o->off, r->len, r->start);
-		goto err;
-	}
-	if(n > o->bufsz){
-		if((b = realloc(o->buf, n)) == nil){
-			werrstr("no memory");
-			goto err;
-		}
-		o->buf = b;
-		o->bufsz = n;
-	}
-	if((x = Bread(o->f, o->buf, n)) != n){
-		werrstr("need %d, got %d; off %d", n, x, o->off);
-		goto err;
-	}
-	o->off += n;
-
-	return o->buf;
-err:
-	return nil;
-}
-
-static int
-otfarray(Otf *o, void **arr_, void *fun_, int elsz, int num)
-{
-	int i;
-	int (*fun)(Otf*, void*);
-	u8int *arr;
-
-	if((arr = calloc(num, elsz)) == nil){
-		werrstr("no memory");
-		goto err;
-	}
-	fun = fun_;
-	for(i = 0; i < num; i++){
-		if(fun(o, arr + i*elsz) < 0)
-			goto err;
-	}
-	*arr_ = arr;
-	return 0;
-err:
-	free(arr);
-	return -1;
-}
-
-static int
-read_ComponentGlyph(Otf *o, ComponentGlyph **out, int instr)
-{
-	ComponentGlyph *v;
-	u8int *b;
-
-	if((v = calloc(1, sizeof(*v))) == nil){
-nomem:
-		werrstr("no memory");
-		goto err;
-	}
-
-	if((b = otfreadn(o, 2*2)) == nil)
-		goto err;
-	v->flags = b[0]<<8 | b[1];
-	v->glyphIndex = b[2]<<8 | b[3];
-	instr |= v->flags & CGLYPH_FL_INSTRUCTIONS;
-
-	if((v->flags & CGLYPH_FL_WORD_ARGS) != 0 && (b = otfreadn(o, 2*2)) != nil){
-		v->arg1.u16 = b[0]<<8 | b[1];
-		v->arg2.u16 = b[2]<<8 | b[3];
-	}else if((b = otfreadn(o, 2)) != nil){
-		v->arg1.u8 = b[0];
-		v->arg2.u8 = b[1];
-	}
-	if(b == nil)
-		goto err;
-	if((v->flags & CGLYPH_FL_SCALE) != 0 && (b = otfreadn(o, 2)) != nil){
-		v->scale = (b[0]<<8 | b[1]>>14)+(b[0]<<8 | b[1]&((1<<14)-1))/16384.0;
-	}else if((v->flags & CGLYPH_FL_SCALE_XY) != 0 && (b = otfreadn(o, 2*2)) != nil){
-		v->scaleX = (b[0]<<8 | b[1]>>14)+(b[0]<<8 | b[1]&((1<<14)-1))/16384.0;
-		v->scaleY = (b[2]<<8 | b[3]>>14)+(b[2]<<8 | b[3]&((1<<14)-1))/16384.0;
-	}else if((v->flags & CGLYPH_FL_2X2_TRANSFORM) != 0 && (b = otfreadn(o, 2*2*2)) != nil){
-		v->scaleX = (b[0]<<8 | b[1]>>14)+(b[0]<<8 | b[1]&((1<<14)-1))/16384.0;
-		v->scale01 = (b[2]<<8 | b[3]>>14)+(b[2]<<8 | b[3]&((1<<14)-1))/16384.0;
-		v->scale10 = (b[4]<<8 | b[5]>>14)+(b[4]<<8 | b[5]&((1<<14)-1))/16384.0;
-		v->scaleY = (b[6]<<8 | b[7]>>14)+(b[6]<<8 | b[7]&((1<<14)-1))/16384.0;
-	}
-	if(b == nil)
-		goto err;
-	if((v->flags & CGLYPH_FL_MORE_COMPONENTS) != 0){
-		if(read_ComponentGlyph(o, &v->next, instr) != 0){
-			free(v);
-			return -1;
-		}
-	}else if(instr != 0){
-		if((b = otfreadn(o, 2)) == nil)
-			goto err;
-		v->numInstr = b[0]<<8 | b[1];
-		if((v->instr = malloc(v->numInstr)) == nil)
-			goto nomem;
-		if(v->numInstr > 0 && (b = otfreadn(o, v->numInstr)) == nil)
-			goto err;
-		memcpy(v->instr, b, v->numInstr);
-	}
-	*out = v;
-	return 0;
-err:
-	if(v != nil)
-		free(v->instr);
-	free(v);
-	werrstr("ComponentGlyph: %r");
-	return -1;
-}
-
-enum {
-	GLYPH_FL_ON_CURVE_POINT = 1<<0,
-	GLYPH_FL_X_SHORT_VECTOR = 1<<1,
-	GLYPH_FL_Y_SHORT_VECTOR = 1<<2,
-	GLYPH_FL_REPEAT = 1<<3,
-	GLYPH_FL_X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR = 1<<4,
-	GLYPH_FL_Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR = 1<<5,
-	GLYPH_FL_OVERLAP_SIMPLE = 1<<6,
-};
-
-static int
-read_SimpleGlyph(Otf *o, SimpleGlyph **out)
-{
-	SimpleGlyph *v;
-	u8int *b, *flags;
-	int i, n, nflags, c;
-
-	flags = nil;
-	if((v = calloc(1, sizeof(*v))) == nil){
-nomem:
-		werrstr("no memory");
-		goto err;
-	}
-
-	n = o->numberOfContours;
-	if((b = otfreadn(o, 2*n+2)) == nil)
-		goto err;
-	if((v->endPtsOfContours = malloc(2*n)) == nil)
-		goto nomem;
-	for(int i = 0; i < n; i++)
-		v->endPtsOfContours[i] = b[2*i+0]<<8 | b[2*i+1];
-
-	v->instructionLength = b[2*n+0]<<8 | b[2*n+1];
-	n = v->instructionLength;
-	if((b = otfreadn(o, n)) == nil)
-		goto err;
-	if((v->instructions = malloc(n)) == nil)
-		goto nomem;
-	memcpy(v->instructions, b, n);
-
-	if((flags = malloc(v->numPoints)) == nil) /* not supposed to be more than that, 64Kb max */
-		goto nomem;
-	for(nflags = 0; nflags < v->numPoints;){
-		if((b = otfreadn(o, 1)) == nil)
-			goto err;
-		flags[nflags] = *b;
-		if((flags[nflags] & GLYPH_FL_REPEAT) != 0){
-			flags[nflags] ^= GLYPH_FL_REPEAT;
-			if((b = otfreadn(o, 1)) == nil)
-				goto err;
-			if(nflags + 1 + *b > v->numPoints){
-				werrstr("repeat overflow");
-				goto err;
-			}
-			memset(flags+nflags+1, flags[nflags], *b);
-			nflags += 1 + *b;
-		}else{
-			nflags++;
-		}
-	}
-
-	v->numPoints = v->endPtsOfContours[o->numberOfContours-1];
-	if((v->points = calloc(v->numPoints, sizeof(*v->points))) == nil)
-		goto nomem;
-
-	/* flags first */
-	for(i = n = 0; i < nflags && n < v->numPoints; i++, n++)
-		v->points[n].onCurve = (flags[i] & GLYPH_FL_ON_CURVE_POINT) != 0;
-
-	/* read packed x coordinates */
-	c = 0;
-	for(i = n = 0; i < nflags && n < v->numPoints; i++){
-		if((flags[i] & GLYPH_FL_X_SHORT_VECTOR) != 0){
-			if((b = otfreadn(o, 1)) == nil)
-				goto err;
-			c = *b;
-			if((flags[i] & GLYPH_FL_X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR) == 0)
-				c = -c;
-			v->points[n++].x = c;
-		}else if((flags[i] & GLYPH_FL_X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR) != 0){
-			v->points[n++].x = c;
-		}else{
-			if((b = otfreadn(o, 2)) == nil)
-				goto err;
-			c += (s16int)(b[0]<<8 | b[1]);
-			v->points[n++].x = c;
-		}
-	}
-
-	/* read packed y coordinates */
-	c = 0;
-	for(i = n = 0; i < nflags && n < v->numPoints; i++){
-		if((flags[i] & GLYPH_FL_Y_SHORT_VECTOR) != 0){
-			if((b = otfreadn(o, 1)) == nil)
-				goto err;
-			c = *b;
-			if((flags[i] & GLYPH_FL_Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR) == 0)
-				c = -c;
-			v->points[n++].y = c;
-		}else if((flags[i] & GLYPH_FL_Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR) != 0){
-			v->points[n++].y = c;
-		}else{
-			if((b = otfreadn(o, 2)) == nil)
-				goto err;
-			c += (s16int)(b[0]<<8 | b[1]);
-			v->points[n++].y = c;
-		}
-	}
-
-	free(flags);
-	*out = v;
-	return 0;
-err:
-	free(flags);
-	if(v != nil){
-		free(v->endPtsOfContours);
-		free(v->instructions);
-		free(v->points);
-	}
-	free(v);
-	werrstr("SimpleGlyph: %r");
-	return -1;
-}
-
-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);
-	if(v == 0)
-		return fmtprint(f, "<nil>");
-	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);
-}
--