shithub: fnt

Download patch

ref: dc095bf883919052348b9f8ba907bb3807e2283d
parent: 0e24bab6a96841fd3d8752ef0d8916f7659d355e
author: Sigrid Solveig Haflínudóttir <sigrid@ftrv.se>
date: Tue Jun 25 00:22:24 EDT 2024

add glyf reading logic (untested)

--- a/gen.rkt
+++ b/gen.rkt
@@ -566,6 +566,57 @@
 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;
+};
+
 EOF
                )
        (printf (format gen-h))
--- a/otf.c
+++ b/otf.c
@@ -821,69 +821,9 @@
 }
 
 int
-read_SimpleGlyph(Otf *o, SimpleGlyph *v)
+read_Glyf(Otf *o, Glyf *v)
 {
 	u8int *b;
-	if((b = otfreadn(o, o->numberOfContours*2)) == nil)
-		goto err;
-	v->endPtsOfContours = malloc(o->numberOfContours*sizeof(*v->endPtsOfContours));
-	for(int i = 0; i < o->numberOfContours; i++)
-		v->endPtsOfContours[i] = b[0+i*2]<<8 | b[1+i*2];
-	if((b = otfreadn(o, 2)) == nil)
-		goto err;
-	v->instructionLength = b[0]<<8 | b[1];
-	if((b = otfreadn(o, v->instructionLength*1)) == nil)
-		goto err;
-	v->instructions = malloc(v->instructionLength*sizeof(*v->instructions));
-	for(int i = 0; i < v->instructionLength; i++)
-		v->instructions[i] = b[0+i*1];
-	if((b = otfreadn(o, 1)) == nil)
-		goto err;
-	v->flags = b[0];
-	return 0;
-err:
-	werrstr("%s: %r", "SimpleGlyph");
-	return -1;
-}
-
-void
-print_SimpleGlyph(Biobuf *f, int indent, Otf *o, SimpleGlyph *v)
-{
-	for(int i = 0; i < o->numberOfContours; i++)
-		Bprint(f, "%*s%s[%d]: %ud\n", indent, "", "endPtsOfContours", i, v->endPtsOfContours[i]);
-	Bprint(f, "%*s%s: %ud\n", indent, "", "instructionLength", v->instructionLength);
-	for(int i = 0; i < v->instructionLength; i++)
-		Bprint(f, "%*s%s[%d]: %ud\n", indent, "", "instructions", i, v->instructions[i]);
-	Bprint(f, "%*s%s: %#ux%s%s%s%s%s%s%s\n", indent, "", "flags", v->flags, (v->flags&GLYPH_FL_ON_CURVE_POINT)?" GLYPH_FL_ON_CURVE_POINT":"", (v->flags&GLYPH_FL_X_SHORT_VECTOR)?" GLYPH_FL_X_SHORT_VECTOR":"", (v->flags&GLYPH_FL_Y_SHORT_VECTOR)?" GLYPH_FL_Y_SHORT_VECTOR":"", (v->flags&GLYPH_FL_REPEAT)?" GLYPH_FL_REPEAT":"", (v->flags&GLYPH_FL_X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR)?" GLYPH_FL_X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR":"", (v->flags&GLYPH_FL_Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR)?" GLYPH_FL_Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR":"", (v->flags&GLYPH_FL_OVERLAP_SIMPLE)?" GLYPH_FL_OVERLAP_SIMPLE":"");
-	USED(o);
-}
-
-int
-read_CompositeGlyph(Otf *o, CompositeGlyph *v)
-{
-	u8int *b;
-	if((b = otfreadn(o, 4)) == nil)
-		goto err;
-	v->flags = b[0]<<8 | b[1];
-	v->glyphIndex = b[2]<<8 | b[3];
-	return 0;
-err:
-	werrstr("%s: %r", "CompositeGlyph");
-	return -1;
-}
-
-void
-print_CompositeGlyph(Biobuf *f, int indent, Otf *o, CompositeGlyph *v)
-{
-	Bprint(f, "%*s%s: %#ux%s%s%s%s%s%s%s%s%s%s%s%s\n", indent, "", "flags", v->flags, (v->flags&CGLYPH_FL_WORD_ARGS)?" CGLYPH_FL_WORD_ARGS":"", (v->flags&CGLYPH_FL_SIGNED_XY)?" CGLYPH_FL_SIGNED_XY":"", (v->flags&CGLYPH_FL_ROUND_TO_GRID_XY)?" CGLYPH_FL_ROUND_TO_GRID_XY":"", (v->flags&CGLYPH_FL_SCALE)?" CGLYPH_FL_SCALE":"", (v->flags&CGLYPH_FL_MORE_COMPONENTS)?" CGLYPH_FL_MORE_COMPONENTS":"", (v->flags&CGLYPH_FL_SCALE_XY)?" CGLYPH_FL_SCALE_XY":"", (v->flags&CGLYPH_FL_2X2_TRANSFORM)?" CGLYPH_FL_2X2_TRANSFORM":"", (v->flags&CGLYPH_FL_INSTRUCTIONS)?" CGLYPH_FL_INSTRUCTIONS":"", (v->flags&CGLYPH_FL_METRICS)?" CGLYPH_FL_METRICS":"", (v->flags&CGLYPH_FL_OVERLAP_COMPOUND)?" CGLYPH_FL_OVERLAP_COMPOUND":"", (v->flags&CGLYPH_FL_SCALED_COMPONENT_OFFSET)?" CGLYPH_FL_SCALED_COMPONENT_OFFSET":"", (v->flags&CGLYPH_FL_UNSCALED_COMPONENT_OFFSET)?" CGLYPH_FL_UNSCALED_COMPONENT_OFFSET":"");
-	Bprint(f, "%*s%s: %ud\n", indent, "", "glyphIndex", v->glyphIndex);
-	USED(o);
-}
-
-int
-read_GlyfHeader(Otf *o, GlyfHeader *v)
-{
-	u8int *b;
 	if((b = otfreadn(o, 10)) == nil)
 		goto err;
 	v->numberOfContours = b[0]<<8 | b[1];
@@ -892,26 +832,18 @@
 	v->yMin = b[4]<<8 | b[5];
 	v->xMax = b[6]<<8 | b[7];
 	v->yMax = b[8]<<8 | b[9];
-	if(v->numberOfContours > 0){
-		if(read_SimpleGlyph(o, &v->simpleGlyph) < 0){
-			werrstr("%s: %r", "simpleGlyph");
-			goto err;
-		}
-	}
-	if(v->numberOfContours < 0){
-		if(read_CompositeGlyph(o, &v->compositeGlyph) < 0){
-			werrstr("%s: %r", "compositeGlyph");
-			goto err;
-		}
-	}
+	if(v->numberOfContours < 0 && read_ComponentGlyph(o, &v->component, 0) < 0)
+		goto err;
+	if(v->numberOfContours > 0 && read_SimpleGlyph(o, &v->simple) < 0)
+		goto err;
 	return 0;
 err:
-	werrstr("%s: %r", "GlyfHeader");
+	werrstr("%s: %r", "Glyf");
 	return -1;
 }
 
 void
-print_GlyfHeader(Biobuf *f, int indent, Otf *o, GlyfHeader *v)
+print_Glyf(Biobuf *f, int indent, Otf *o, Glyf *v)
 {
 	Bprint(f, "%*s%s: %d\n", indent, "", "numberOfContours", v->numberOfContours);
 	Bprint(f, "%*s%s: %d\n", indent, "", "xMin", v->xMin);
@@ -918,14 +850,6 @@
 	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);
-	if(v->numberOfContours > 0){
-		Bprint(f, "%*s%s:\n", indent, "", "simpleGlyph");
-		print_SimpleGlyph(f, indent+indentΔ, o, &v->simpleGlyph);
-	}
-	if(v->numberOfContours < 0){
-		Bprint(f, "%*s%s:\n", indent, "", "compositeGlyph");
-		print_CompositeGlyph(f, indent+indentΔ, o, &v->compositeGlyph);
-	}
 	USED(o);
 }
 
@@ -5072,7 +4996,7 @@
 			case (u32int)('l'<<24|'o'<<16|'c'<<8|'a'):
 				{
 					static int retried = 0;
-					if(v->head == nil){
+					if(v->head == nil || v->maxp == nil){
 						if(retried){
 							werrstr("%s: deps missing", "TableLoca");
 							goto err;
--- a/otf.h
+++ b/otf.h
@@ -1,6 +1,57 @@
 /* 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;
+};
 typedef struct SubHeader SubHeader;
 typedef struct MapGroup MapGroup;
 typedef struct SubtableCmap0 SubtableCmap0;
@@ -21,9 +72,7 @@
 typedef struct TableCmap TableCmap;
 typedef struct TableHead TableHead;
 typedef struct TableHhea TableHhea;
-typedef struct SimpleGlyph SimpleGlyph;
-typedef struct CompositeGlyph CompositeGlyph;
-typedef struct GlyfHeader GlyfHeader;
+typedef struct Glyf Glyf;
 typedef struct LongHorMetric LongHorMetric;
 typedef struct TableMaxp TableMaxp;
 typedef struct TableHmtx TableHmtx;
@@ -364,63 +413,18 @@
 int read_TableHhea(Otf *o, TableHhea *v);
 void print_TableHhea(Biobuf *f, int indent, Otf *o, TableHhea *v);
 
-enum { // SimpleGlyph
-	// flags
-	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,
-};
-
-struct SimpleGlyph {
-	u16int *endPtsOfContours;
-	u16int instructionLength;
-	u8int *instructions;
-	u8int flags;
-};
-
-int read_SimpleGlyph(Otf *o, SimpleGlyph *v);
-void print_SimpleGlyph(Biobuf *f, int indent, Otf *o, SimpleGlyph *v);
-
-enum { // CompositeGlyph
-	// flags
-	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 CompositeGlyph {
-	u16int flags;
-	u16int glyphIndex;
-};
-
-int read_CompositeGlyph(Otf *o, CompositeGlyph *v);
-void print_CompositeGlyph(Biobuf *f, int indent, Otf *o, CompositeGlyph *v);
-
-struct GlyfHeader {
+struct Glyf {
 	s16int numberOfContours;
 	s16int xMin;
 	s16int yMin;
 	s16int xMax;
 	s16int yMax;
-	SimpleGlyph simpleGlyph;
-	CompositeGlyph compositeGlyph;
+	ComponentGlyph *component;
+	SimpleGlyph *simple;
 };
 
-int read_GlyfHeader(Otf *o, GlyfHeader *v);
-void print_GlyfHeader(Biobuf *f, int indent, Otf *o, GlyfHeader *v);
+int read_Glyf(Otf *o, Glyf *v);
+void print_Glyf(Biobuf *f, int indent, Otf *o, Glyf *v);
 
 struct LongHorMetric {
 	u16int advanceWidth;
--- a/otf.rkt
+++ b/otf.rkt
@@ -201,53 +201,20 @@
          {uint16 numberOfHMetrics ->o}
          #:tag "hhea")
 
-(define simpleGlyphFlags
-  #hash((0 . GLYPH_FL_ON_CURVE_POINT)
-        (1 . GLYPH_FL_X_SHORT_VECTOR)
-        (2 . GLYPH_FL_Y_SHORT_VECTOR)
-        (3 . GLYPH_FL_REPEAT)
-        (4 . GLYPH_FL_X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR)
-        (5 . GLYPH_FL_Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR)
-        (6 . GLYPH_FL_OVERLAP_SIMPLE)))
-
-(mkcmplx SimpleGlyph
-         {uint16 endPtsOfContours [o->numberOfContours]}
-         {uint16 instructionLength}
-         {uint8 instructions [instructionLength]}
-         {uint8 flags hex (bits simpleGlyphFlags)} ; FIXME this is a (packed) array
-         #;{uint8/uint16 xCoordinates [?]}
-         #;{uint8/uint16 yCoordinates [?]})
-
-(define compositeGlyphFlags
-  #hash((0 . CGLYPH_FL_WORD_ARGS)
-        (1 . CGLYPH_FL_SIGNED_XY)
-        (2 . CGLYPH_FL_ROUND_TO_GRID_XY)
-        (3 . CGLYPH_FL_SCALE)
-        (5 . CGLYPH_FL_MORE_COMPONENTS)
-        (6 . CGLYPH_FL_SCALE_XY)
-        (7 . CGLYPH_FL_2X2_TRANSFORM)
-        (8 . CGLYPH_FL_INSTRUCTIONS)
-        (9 . CGLYPH_FL_METRICS)
-        (10 . CGLYPH_FL_OVERLAP_COMPOUND)
-        (11 . CGLYPH_FL_SCALED_COMPONENT_OFFSET)
-        (12 . CGLYPH_FL_UNSCALED_COMPONENT_OFFSET)))
-
-(mkcmplx CompositeGlyph
-         {uint16 flags hex (bits compositeGlyphFlags)}
-         {uint16 glyphIndex}
-         #;{uint8/int8/uint16/int16 argument1} ; FIXME are you fucking kidding me
-         #;{uint8/int8/uint16/int16 argument2}
-         ; FIXME there is more shit here
-         )
-
-(mkcmplx GlyfHeader
-         {int16 numberOfContours ->o}
-         {int16 xMin}
-         {int16 yMin}
-         {int16 xMax}
-         {int16 yMax}
-         {SimpleGlyph simpleGlyph (> numberOfContours 0)}
-         {CompositeGlyph compositeGlyph (< numberOfContours 0)})
+(mkcmplx
+ Glyf
+ {int16 numberOfContours ->o}
+ {int16 xMin}
+ {int16 yMin}
+ {int16 xMax}
+ {int16 yMax}
+ #:extra
+ (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 "if(v->numberOfContours > 0 && read_SimpleGlyph(o, &v->simple) < 0)")
+                   (~a "\tgoto err;")))))
 
 (mkcmplx LongHorMetric {UFWORD advanceWidth} {FWORD lsb})
 
--- a/otfpriv.h
+++ b/otfpriv.h
@@ -221,6 +221,195 @@
 	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