shithub: fnt

Download patch

ref: 360b4c5effb93584b29179241df67b725c54d7b2
parent: 094b3e1d61c9c7cd85c3dfba8a6b40185004eed3
author: Sigrid Solveig Haflínudóttir <sigrid@ftrv.se>
date: Wed Jul 24 19:56:24 EDT 2024

more bitmap font functionality; split ppem into ppemX and ppemY

--- a/gen.rkt
+++ b/gen.rkt
@@ -292,37 +292,38 @@
         identity))
   (define is-ptr (and (cmplx? t) (field-offset f) (not cnt)))
   (define lst
-    (flatten (list (if (type? t)
-                       (~a "f->print(f->aux, \"%*s%s: "
-                           verb
-                           bits-verbs
-                           "\\n\", indent, \"\", \""
-                           (field-name f)
-                           "\", "
-                           (fmtarg (~a "v->" (field-name f) array-index))
-                           bits-args
-                           ");")
-                       (list (if cnt
-                                 empty
-                                 (~a "f->print(f->aux, \"%*s%s:\\n\", indent, \"\", \""
-                                     (field-name f)
-                                     "\");"))
-                             (if is-ptr (~a "if(v->" (field-name f) " != nil)") empty)
-                             (~a (if is-ptr "	" "")
-                                 "print_"
-                                 (cmplx-name t)
-                                 "(f, indent+indentΔ, o, "
-                                 (if is-ptr "" "&")
-                                 "v->"
-                                 (field-name f)
-                                 array-index
-                                 ");"))))))
+    (flatten
+     (list
+      (if (type? t)
+          (list (~a "f->print(f->aux, \"%*s%s: "
+                    verb
+                    bits-verbs
+                    "\\n\", indent, \"\", \""
+                    (field-name f)
+                    "\", "
+                    (fmtarg (~a "v->" (field-name f) array-index))
+                    bits-args
+                    ");")
+                (if (field-context? f) (~a "o->" (field-name f) " = v->" (field-name f) ";") empty))
+          (list (if cnt
+                    empty
+                    (~a "f->print(f->aux, \"%*s%s:\\n\", indent, \"\", \"" (field-name f) "\");"))
+                (if is-ptr (~a "if(v->" (field-name f) " != nil)") empty)
+                (~a (if is-ptr "	" "")
+                    "print_"
+                    (cmplx-name t)
+                    "(f, indent+indentΔ, o, "
+                    (if is-ptr "" "&")
+                    "v->"
+                    (field-name f)
+                    array-index
+                    ");"))))))
   (if (field-unused? f) empty (wrap-cond-c (field-cond f) (array-loop lst))))
 
 (define (filter-extra extra key)
   (flatten (filter-map (λ (e) (and (eq? (car e) key) (cdr e))) extra)))
 
-(define-struct cmplx (name fields tag extra after)
+(define-struct cmplx (name fields tag extra after optional)
   #:transparent
   #:methods gen:code
   [(define/generic super-gen-h gen-h)
@@ -476,7 +477,8 @@
         fields:expr ...+
         (~optional (~seq #:tag tag))
         (~optional (~seq #:extra extra:expr))
-        (~optional (~seq #:after after)))
+        (~optional (~seq #:after after))
+        (~optional (~seq #:optional optional)))
      #:declare tag (expr/c #'tag/c #:name "table tag")
      (begin
        (set! typenames (cons (syntax-e #`typ) typenames))
@@ -487,7 +489,8 @@
                          (mkfields [~@ fields] ...)
                          tag-
                          (~? (~@ extra) empty)
-                         (~? (~@ after) empty)))
+                         (~? (~@ after) empty)
+                         (~? (~@ optional) #f)))
            (set! cmplxs (append cmplxs (list typ)))
            (when tag-
              (set! tagged (append tagged (list typ))))))]))
--- a/otf.c.in
+++ b/otf.c.in
@@ -248,6 +248,7 @@
 	f->print(a, "%*s%s: %d\n", indent, "", "format", v->format);
 	f->print(a, "%*s%s: %d\n", indent, "", "offset", v->offset);
 	if(v->size > 0){
+		f->print(a, "%*s%s: %d\n", indent, "", "pitchBits", v->pitchBits);
 		f->print(a, "%*s%s: %d\n", indent, "", "size", v->size);
 		f->print(a, "%*s%s: ", indent, "", "image");
 		for(i = 0; i < v->size; i++)
@@ -254,8 +255,10 @@
 			f->print(a, "%02"PRIx8, v->image[i]);
 		f->print(a, "\n");
 	}
+	f->print(a, "%*s%s: %d\n", indent, "", "ppemX", v->ppemX);
+	f->print(a, "%*s%s: %d\n", indent, "", "ppemY", v->ppemY);
 	if(v->numComponents > 0){
-		f->print(a, "%*s%s: %d\n", indent, "", "numComponents");
+		f->print(a, "%*s%s: %d\n", indent, "", "numComponents", v->numComponents);
 		f->print(a, "%*s%s:\n", indent, "", "components");
 		for(i = 0; i < v->numComponents; i++){
 			f->print(a, "%*s%d:\n", indent+indentΔ, "", i);
@@ -551,7 +554,7 @@
 bitebdt(Otf *o, Glyf *g, BitmapGlyph *bg)
 {
 	u8int *b;
-	int i;
+	int i, n;
 
 	if(o->ebdt == nil){
 		for(i = 0; i < o->td.numTables; i++){
@@ -579,7 +582,6 @@
 		g->yMin = sm.bearingY - sm.height;
 		g->xMax = sm.bearingX + sm.width;
 		g->yMax = sm.bearingY;
-		bg->size -= 6;
 	}else if(bg->format == 6 || bg->format == 7 || bg->format == 9){
 		BigGlyphMetrics bm;
 		if(read_BigGlyphMetrics(o, &bm) < 0)
@@ -591,24 +593,34 @@
 		g->yMin = -(int)bm.horiBearingY;
 		g->xMax += g->xMin;
 		g->yMax += g->yMin;
-		bg->size -= 8;
 	}
 
 	bg->numComponents = 0;
+	if(bg->format == 2 || bg->format == 5 || bg->format == 7){
+		/* bit-aligned - last row is padded to a full byte */
+		bg->pitchBits = g->xMax - g->xMin;
+		bg->size = (bg->pitchBits*(g->yMax-g->yMin)*bg->bitDepth + 7)/8;
+	}else{
+		/* byte-aligned - every row is byte-aligned */
+		n = ((g->xMax-g->xMin)*bg->bitDepth + 7)/8;
+		bg->size = n * (g->yMax-g->yMin);
+		bg->pitchBits = n * 8;
+	}
+
 	switch(bg->format){
 	case 8:
 		if(otfreadn(o, 1) == nil) /* skip uint8 pad */
 			goto err;
-		bg->size -= 1;
 		/* fallthrough */
 	case 9:
 		if((b = otfreadn(o, 2)) == nil)
 			goto err;
 		bg->numComponents = b[0]<<8 | b[1];
-		bg->size -= 2;
 		if(otfarray(o, &bg->components, read_EbdtComponent, sizeof(EbdtComponent), bg->numComponents) < 0)
 			goto err;
-		bg->size -= 4*bg->numComponents;
+		/* no image, both 8 and 9 are component */
+		bg->pitchBits = 0;
+		bg->size = 0;
 		/* fallthrough */
 	case 1:
 	case 2:
@@ -615,11 +627,14 @@
 	case 5:
 	case 6:
 	case 7:
-		if((b = otfreadn(o, bg->size)) == nil)
-			goto err;
 		g->bitmap = malloc(sizeof(*bg) + bg->size);
 		memcpy(g->bitmap, bg, sizeof(*bg));
-		memcpy(g->bitmap->image, b, bg->size);
+		if(bg->size > 0){
+			if((b = otfreadn(o, bg->size)) == nil)
+				goto err;
+			memcpy(g->bitmap->image, b, bg->size);
+		}
+		break;
 	}
 	otfpoprange(o);
 	otfpoprange(o);
@@ -646,8 +661,10 @@
 	for(i = 0; i < (int)o->td.eblc->numSizes; i++, bs++){
 		if(g->index >= bs->startGlyphIndex && g->index <= bs->endGlyphIndex){
 			b.bitDepth = bs->bitDepth;
+			b.ppemX = bs->ppemX;
+			b.ppemY = bs->ppemY;
 			isr = bs->indexSubtableList;
-			for(j = 0; j < (int)bs->numberOfIndexSubtables; j++){
+			for(j = 0; j < (int)bs->numberOfIndexSubtables; j++, isr++){
 				if(g->index >= isr->firstGlyphIndex && g->index <= isr->lastGlyphIndex){
 					is = isr->indexSubtable;
 					if(is == nil)
@@ -780,7 +797,6 @@
 	}
 	otfpoprange(o);
 	otfpoprange(o);
-	g->type = g->numberOfContours < 0 ? GLYPH_COMPONENT : GLYPH_SIMPLE;
 
 	return g;
 err:
--- a/otf.h.in
+++ b/otf.h.in
@@ -51,6 +51,9 @@
 	int format;
 	int offset;
 	int size;
+	int pitchBits;
+	int ppemX;
+	int ppemY;
 	int numComponents;
 	EbdtComponent *components;
 	u8int image[];
@@ -129,4 +132,4 @@
 Rune otfglyph2rune(Otf *o, int g);
 
 Glyf *otfglyf(Otf *o, int index);
-GlyfImage *otfdrawglyf(Otf *o, Glyf *g, double ppem, double scaleX, double scaleY);
+GlyfImage *otfdrawglyf(Otf *o, Glyf *g, double ppemX, double ppemY);
--- a/otf.rkt
+++ b/otf.rkt
@@ -201,48 +201,47 @@
          {uint16 numberOfHMetrics ->o}
          #:tag "hhea")
 
-(mkcmplx EbdtComponent
-  {uint16 glyphID}
-  {int8 xOffset}
-  {int8 yOffset})
+(mkcmplx EbdtComponent {uint16 glyphID} {int8 xOffset} {int8 yOffset})
 
-(mkcmplx
- Glyf
- {int16 numberOfContours ->o}
- {int16 xMin}
- {int16 yMin}
- {int16 xMax}
- {int16 yMax}
- #:extra
- (list (cons 'field (list (~a "int index;")
-                          (~a "int type;")
-                          (~a "union {");
-                          (~a "	ComponentGlyph *component;")
-                          (~a "	SimpleGlyph *simple;")
-                          (~a "	BitmapGlyph *bitmap;")
-                          (~a "};")))
-       (cons 'read
-             (list (~a "if(v->numberOfContours < 0){")
-                   (~a "	if(read_ComponentGlyph(o, &v->component, 0) < 0)")
-                   (~a "		goto err;")
-                   (~a "	v->type = GLYPH_COMPONENT;")
-                   (~a "}else if(v->numberOfContours > 0){")
-                   (~a "	if(read_SimpleGlyph(o, &v->simple) < 0)")
-                   (~a "		goto err;")
-                   (~a "	v->type = GLYPH_SIMPLE;")
-                   (~a "}else if(o->td.eblc != nil && o->td.ebdt != nil)")
-                   (~a "	v->type = GLYPH_BITMAP;")))
-        (cons 'print
-             (list (~a "if(v->type == GLYPH_BITMAP){")
-                   (~a "	f->print(f->aux, \"%*s%s:\\n\", indent, \"\", \"bitmap\");")
-                   (~a "	print_BitmapGlyph(f, indent+indentΔ, o, v->bitmap);")
-                   (~a "}else if(v->type == GLYPH_COMPONENT){")
-                   (~a "	f->print(f->aux, \"%*s%s:\\n\", indent, \"\", \"component\");")
-                   (~a "	print_ComponentGlyph(f, indent+indentΔ, o, v->component);")
-                   (~a "}else if(v->type == GLYPH_SIMPLE){")
-                   (~a "	f->print(f->aux, \"%*s%s:\\n\", indent, \"\", \"simple\");")
-                   (~a "	print_SimpleGlyph(f, indent+indentΔ, o, v->simple);")
-                   (~a "}")))))
+(mkcmplx Glyf
+         {int16 numberOfContours ->o}
+         {int16 xMin}
+         {int16 yMin}
+         {int16 xMax}
+         {int16 yMax}
+         #:extra
+         (list (cons 'field
+                     (list (~a "int index;")
+                           (~a "int type;")
+                           (~a "union {") ;
+                           (~a "	ComponentGlyph *component;")
+                           (~a "	SimpleGlyph *simple;")
+                           (~a "	BitmapGlyph *bitmap;")
+                           (~a "};")))
+               (cons 'read
+                     (list (~a "if(v->numberOfContours < 0){")
+                           (~a "	if(read_ComponentGlyph(o, &v->component, 0) < 0)")
+                           (~a "		goto err;")
+                           (~a "	v->type = GLYPH_COMPONENT;")
+                           (~a "}else if(v->numberOfContours > 0){")
+                           (~a "	if(read_SimpleGlyph(o, &v->simple) < 0)")
+                           (~a "		goto err;")
+                           (~a "	v->type = GLYPH_SIMPLE;")
+                           (~a "}else if(o->td.eblc != nil && o->td.ebdt != nil)")
+                           (~a "	v->type = GLYPH_BITMAP;")
+                           (~a "else")
+                           (~a "	v->type = GLYPH_EMPTY;")))
+               (cons 'print
+                     (list (~a "if(v->type == GLYPH_BITMAP){")
+                           (~a "	f->print(f->aux, \"%*s%s:\\n\", indent, \"\", \"bitmap\");")
+                           (~a "	print_BitmapGlyph(f, indent+indentΔ, o, v->bitmap);")
+                           (~a "}else if(v->type == GLYPH_COMPONENT){")
+                           (~a "	f->print(f->aux, \"%*s%s:\\n\", indent, \"\", \"component\");")
+                           (~a "	print_ComponentGlyph(f, indent+indentΔ, o, v->component);")
+                           (~a "}else if(v->type == GLYPH_SIMPLE){")
+                           (~a "	f->print(f->aux, \"%*s%s:\\n\", indent, \"\", \"simple\");")
+                           (~a "	print_SimpleGlyph(f, indent+indentΔ, o, v->simple);")
+                           (~a "}")))))
 
 (mkcmplx LongHorMetric {UFWORD advanceWidth} {FWORD lsb})
 
@@ -672,7 +671,8 @@
          {Offset16 shortOffsets [+ o->numGlyphs 1] (== o->indexToLocFormat 0)}
          {Offset32 longOffsets [+ o->numGlyphs 1] (== o->indexToLocFormat 1)}
          #:tag "loca"
-         #:after (list TableHead TableMaxp))
+         #:after (list TableHead TableMaxp)
+         #:optional #t)
 
 (define variationAxisRecordFlags #hash((0 . VARIATIONAXISRECORD_FL_HIDDEN_AXIS)))
 
@@ -954,7 +954,8 @@
           (~a "	v->" (ptr c) " = calloc(1, sizeof(" (cmplx-name c) "));")
           (~a "	if(read_" (cmplx-name c) "(o, v->" (ptr c) ") < 0){")
           (~a "		free(v->" (ptr c) ");")
-          (~a "		goto err;")
+          (~a "		v->" (ptr c) " = nil;")
+          (~a "		" (if (cmplx-optional c) "break;" "goto err;"))
           (~a "	}")
           (~a "	rec->parsed = v->" (ptr c) ";")
           (~a "	rec->print = (void*)print_" (cmplx-name c) ";")
--- a/plan9/otf.c
+++ b/plan9/otf.c
@@ -249,6 +249,7 @@
 	f->print(a, "%*s%s: %d\n", indent, "", "format", v->format);
 	f->print(a, "%*s%s: %d\n", indent, "", "offset", v->offset);
 	if(v->size > 0){
+		f->print(a, "%*s%s: %d\n", indent, "", "pitchBits", v->pitchBits);
 		f->print(a, "%*s%s: %d\n", indent, "", "size", v->size);
 		f->print(a, "%*s%s: ", indent, "", "image");
 		for(i = 0; i < v->size; i++)
@@ -255,8 +256,10 @@
 			f->print(a, "%02"PRIx8, v->image[i]);
 		f->print(a, "\n");
 	}
+	f->print(a, "%*s%s: %d\n", indent, "", "ppemX", v->ppemX);
+	f->print(a, "%*s%s: %d\n", indent, "", "ppemY", v->ppemY);
 	if(v->numComponents > 0){
-		f->print(a, "%*s%s: %d\n", indent, "", "numComponents");
+		f->print(a, "%*s%s: %d\n", indent, "", "numComponents", v->numComponents);
 		f->print(a, "%*s%s:\n", indent, "", "components");
 		for(i = 0; i < v->numComponents; i++){
 			f->print(a, "%*s%d:\n", indent+indentΔ, "", i);
@@ -552,7 +555,7 @@
 bitebdt(Otf *o, Glyf *g, BitmapGlyph *bg)
 {
 	u8int *b;
-	int i;
+	int i, n;
 
 	if(o->ebdt == nil){
 		for(i = 0; i < o->td.numTables; i++){
@@ -580,7 +583,6 @@
 		g->yMin = sm.bearingY - sm.height;
 		g->xMax = sm.bearingX + sm.width;
 		g->yMax = sm.bearingY;
-		bg->size -= 6;
 	}else if(bg->format == 6 || bg->format == 7 || bg->format == 9){
 		BigGlyphMetrics bm;
 		if(read_BigGlyphMetrics(o, &bm) < 0)
@@ -592,24 +594,34 @@
 		g->yMin = -(int)bm.horiBearingY;
 		g->xMax += g->xMin;
 		g->yMax += g->yMin;
-		bg->size -= 8;
 	}
 
 	bg->numComponents = 0;
+	if(bg->format == 2 || bg->format == 5 || bg->format == 7){
+		/* bit-aligned - last row is padded to a full byte */
+		bg->pitchBits = g->xMax - g->xMin;
+		bg->size = (bg->pitchBits*(g->yMax-g->yMin)*bg->bitDepth + 7)/8;
+	}else{
+		/* byte-aligned - every row is byte-aligned */
+		n = ((g->xMax-g->xMin)*bg->bitDepth + 7)/8;
+		bg->size = n * (g->yMax-g->yMin);
+		bg->pitchBits = n * 8;
+	}
+
 	switch(bg->format){
 	case 8:
 		if(otfreadn(o, 1) == nil) /* skip uint8 pad */
 			goto err;
-		bg->size -= 1;
 		/* fallthrough */
 	case 9:
 		if((b = otfreadn(o, 2)) == nil)
 			goto err;
 		bg->numComponents = b[0]<<8 | b[1];
-		bg->size -= 2;
 		if(otfarray(o, &bg->components, read_EbdtComponent, sizeof(EbdtComponent), bg->numComponents) < 0)
 			goto err;
-		bg->size -= 4*bg->numComponents;
+		/* no image, both 8 and 9 are component */
+		bg->pitchBits = 0;
+		bg->size = 0;
 		/* fallthrough */
 	case 1:
 	case 2:
@@ -616,11 +628,14 @@
 	case 5:
 	case 6:
 	case 7:
-		if((b = otfreadn(o, bg->size)) == nil)
-			goto err;
 		g->bitmap = malloc(sizeof(*bg) + bg->size);
 		memcpy(g->bitmap, bg, sizeof(*bg));
-		memcpy(g->bitmap->image, b, bg->size);
+		if(bg->size > 0){
+			if((b = otfreadn(o, bg->size)) == nil)
+				goto err;
+			memcpy(g->bitmap->image, b, bg->size);
+		}
+		break;
 	}
 	otfpoprange(o);
 	otfpoprange(o);
@@ -647,8 +662,10 @@
 	for(i = 0; i < (int)o->td.eblc->numSizes; i++, bs++){
 		if(g->index >= bs->startGlyphIndex && g->index <= bs->endGlyphIndex){
 			b.bitDepth = bs->bitDepth;
+			b.ppemX = bs->ppemX;
+			b.ppemY = bs->ppemY;
 			isr = bs->indexSubtableList;
-			for(j = 0; j < (int)bs->numberOfIndexSubtables; j++){
+			for(j = 0; j < (int)bs->numberOfIndexSubtables; j++, isr++){
 				if(g->index >= isr->firstGlyphIndex && g->index <= isr->lastGlyphIndex){
 					is = isr->indexSubtable;
 					if(is == nil)
@@ -781,7 +798,6 @@
 	}
 	otfpoprange(o);
 	otfpoprange(o);
-	g->type = g->numberOfContours < 0 ? GLYPH_COMPONENT : GLYPH_SIMPLE;
 
 	return g;
 err:
@@ -1958,6 +1974,7 @@
 	f->print(f->aux, "%*s%s: %ud\n", indent, "", "macStyle", v->macStyle);
 	f->print(f->aux, "%*s%s: %ud\n", indent, "", "lowestRecPPEM", v->lowestRecPPEM);
 	f->print(f->aux, "%*s%s: %d\n", indent, "", "indexToLocFormat", v->indexToLocFormat);
+	o->indexToLocFormat = v->indexToLocFormat;
 	USED(o);
 }
 
@@ -2017,6 +2034,7 @@
 	f->print(f->aux, "%*s%s: %d\n", indent, "", "caretOffset", v->caretOffset);
 	f->print(f->aux, "%*s%s: %d\n", indent, "", "metricDataFormat", v->metricDataFormat);
 	f->print(f->aux, "%*s%s: %ud\n", indent, "", "numberOfHMetrics", v->numberOfHMetrics);
+	o->numberOfHMetrics = v->numberOfHMetrics;
 	USED(o);
 }
 
@@ -2066,6 +2084,8 @@
 		v->type = GLYPH_SIMPLE;
 	}else if(o->td.eblc != nil && o->td.ebdt != nil)
 		v->type = GLYPH_BITMAP;
+	else
+		v->type = GLYPH_EMPTY;
 	return 0;
 err:
 	werrstr("%s: %r", "Glyf");
@@ -2076,6 +2096,7 @@
 print_Glyf(Otfile *f, int indent, Otf *o, Glyf *v)
 {
 	f->print(f->aux, "%*s%s: %d\n", indent, "", "numberOfContours", v->numberOfContours);
+	o->numberOfContours = v->numberOfContours;
 	f->print(f->aux, "%*s%s: %d\n", indent, "", "xMin", v->xMin);
 	f->print(f->aux, "%*s%s: %d\n", indent, "", "yMin", v->yMin);
 	f->print(f->aux, "%*s%s: %d\n", indent, "", "xMax", v->xMax);
@@ -2138,6 +2159,7 @@
 print_TableMaxp(Otfile *f, int indent, Otf *o, TableMaxp *v)
 {
 	f->print(f->aux, "%*s%s: %ud\n", indent, "", "numGlyphs", v->numGlyphs);
+	o->numGlyphs = v->numGlyphs;
 	USED(o);
 }
 
@@ -2236,7 +2258,9 @@
 print_NameRecord(Otfile *f, int indent, Otf *o, NameRecord *v)
 {
 	f->print(f->aux, "%*s%s: %ud\n", indent, "", "platformID", v->platformID);
+	o->platformID = v->platformID;
 	f->print(f->aux, "%*s%s: %ud\n", indent, "", "encodingID", v->encodingID);
+	o->encodingID = v->encodingID;
 	f->print(f->aux, "%*s%s: %ud\n", indent, "", "languageID", v->languageID);
 	f->print(f->aux, "%*s%s: %ud\n", indent, "", "nameID", v->nameID);
 	f->print(f->aux, "%*s%s: %ud\n", indent, "", "stringLength", v->stringLength);
@@ -2319,6 +2343,7 @@
 	f->print(f->aux, "%*s%s: %ud\n", indent, "", "version", v->version);
 	f->print(f->aux, "%*s%s: %ud\n", indent, "", "count", v->count);
 	f->print(f->aux, "%*s%s: %ud\n", indent, "", "storageOffset", v->storageOffset);
+	o->storageOffset = v->storageOffset;
 	for(int i = 0; i < v->count; i++){
 		f->print(f->aux, "%*s%s[%d]:\n", indent, "", "nameRecord", i);
 		print_NameRecord(f, indent+indentΔ, o, &v->nameRecord[i]);
@@ -2703,7 +2728,9 @@
 print_IndexSubtableRecord(Otfile *f, int indent, Otf *o, IndexSubtableRecord *v)
 {
 	f->print(f->aux, "%*s%s: %ud\n", indent, "", "firstGlyphIndex", v->firstGlyphIndex);
+	o->firstGlyphIndex = v->firstGlyphIndex;
 	f->print(f->aux, "%*s%s: %ud\n", indent, "", "lastGlyphIndex", v->lastGlyphIndex);
+	o->lastGlyphIndex = v->lastGlyphIndex;
 	f->print(f->aux, "%*s%s: %ud\n", indent, "", "indexSubtableOffset", v->indexSubtableOffset);
 	f->print(f->aux, "%*s%s:\n", indent, "", "indexSubtable");
 	if(v->indexSubtable != nil)
@@ -5034,8 +5061,11 @@
 {
 	f->print(f->aux, "%*s%s: %ud\n", indent, "", "axesArrayOffset", v->axesArrayOffset);
 	f->print(f->aux, "%*s%s: %ud\n", indent, "", "axisCount", v->axisCount);
+	o->axisCount = v->axisCount;
 	f->print(f->aux, "%*s%s: %ud\n", indent, "", "instanceCount", v->instanceCount);
+	o->instanceCount = v->instanceCount;
 	f->print(f->aux, "%*s%s: %ud\n", indent, "", "instanceSize", v->instanceSize);
+	o->instanceSize = v->instanceSize;
 	f->print(f->aux, "%*s%s:\n", indent, "", "axisInstances");
 	if(v->axisInstances != nil)
 		print_AxisInstances(f, indent+indentΔ, o, v->axisInstances);
@@ -5567,9 +5597,12 @@
 {
 	f->print(f->aux, "%*s%s: %ud\n", indent, "", "minorVersion", v->minorVersion);
 	f->print(f->aux, "%*s%s: %ud\n", indent, "", "designAxisSize", v->designAxisSize);
+	o->designAxisSize = v->designAxisSize;
 	f->print(f->aux, "%*s%s: %ud\n", indent, "", "designAxisCount", v->designAxisCount);
+	o->designAxisCount = v->designAxisCount;
 	f->print(f->aux, "%*s%s: %ud\n", indent, "", "designAxesOffset", v->designAxesOffset);
 	f->print(f->aux, "%*s%s: %ud\n", indent, "", "axisValueCount", v->axisValueCount);
+	o->axisValueCount = v->axisValueCount;
 	f->print(f->aux, "%*s%s: %ud\n", indent, "", "offsetToAxisValueOffsets", v->offsetToAxisValueOffsets);
 	if(v->minorVersion > 0)
 		f->print(f->aux, "%*s%s: %ud\n", indent, "", "elidedFallbackNameID", v->elidedFallbackNameID);
@@ -6075,6 +6108,7 @@
 				v->cmap = calloc(1, sizeof(TableCmap));
 				if(read_TableCmap(o, v->cmap) < 0){
 					free(v->cmap);
+					v->cmap = nil;
 					goto err;
 				}
 				rec->parsed = v->cmap;
@@ -6086,6 +6120,7 @@
 				v->head = calloc(1, sizeof(TableHead));
 				if(read_TableHead(o, v->head) < 0){
 					free(v->head);
+					v->head = nil;
 					goto err;
 				}
 				rec->parsed = v->head;
@@ -6097,6 +6132,7 @@
 				v->hhea = calloc(1, sizeof(TableHhea));
 				if(read_TableHhea(o, v->hhea) < 0){
 					free(v->hhea);
+					v->hhea = nil;
 					goto err;
 				}
 				rec->parsed = v->hhea;
@@ -6108,6 +6144,7 @@
 				v->maxp = calloc(1, sizeof(TableMaxp));
 				if(read_TableMaxp(o, v->maxp) < 0){
 					free(v->maxp);
+					v->maxp = nil;
 					goto err;
 				}
 				rec->parsed = v->maxp;
@@ -6133,6 +6170,7 @@
 				v->hmtx = calloc(1, sizeof(TableHmtx));
 				if(read_TableHmtx(o, v->hmtx) < 0){
 					free(v->hmtx);
+					v->hmtx = nil;
 					goto err;
 				}
 				rec->parsed = v->hmtx;
@@ -6144,6 +6182,7 @@
 				v->post = calloc(1, sizeof(TablePost));
 				if(read_TablePost(o, v->post) < 0){
 					free(v->post);
+					v->post = nil;
 					goto err;
 				}
 				rec->parsed = v->post;
@@ -6155,6 +6194,7 @@
 				v->name = calloc(1, sizeof(TableName));
 				if(read_TableName(o, v->name) < 0){
 					free(v->name);
+					v->name = nil;
 					goto err;
 				}
 				rec->parsed = v->name;
@@ -6166,6 +6206,7 @@
 				v->ebdt = calloc(1, sizeof(TableEBDT));
 				if(read_TableEBDT(o, v->ebdt) < 0){
 					free(v->ebdt);
+					v->ebdt = nil;
 					goto err;
 				}
 				rec->parsed = v->ebdt;
@@ -6177,6 +6218,7 @@
 				v->eblc = calloc(1, sizeof(TableEBLC));
 				if(read_TableEBLC(o, v->eblc) < 0){
 					free(v->eblc);
+					v->eblc = nil;
 					goto err;
 				}
 				rec->parsed = v->eblc;
@@ -6188,6 +6230,7 @@
 				v->gdef = calloc(1, sizeof(TableGDEF));
 				if(read_TableGDEF(o, v->gdef) < 0){
 					free(v->gdef);
+					v->gdef = nil;
 					goto err;
 				}
 				rec->parsed = v->gdef;
@@ -6199,6 +6242,7 @@
 				v->gpos = calloc(1, sizeof(TableGPOS));
 				if(read_TableGPOS(o, v->gpos) < 0){
 					free(v->gpos);
+					v->gpos = nil;
 					goto err;
 				}
 				rec->parsed = v->gpos;
@@ -6210,6 +6254,7 @@
 				v->gsub = calloc(1, sizeof(TableGSUB));
 				if(read_TableGSUB(o, v->gsub) < 0){
 					free(v->gsub);
+					v->gsub = nil;
 					goto err;
 				}
 				rec->parsed = v->gsub;
@@ -6221,6 +6266,7 @@
 				v->math = calloc(1, sizeof(TableMATH));
 				if(read_TableMATH(o, v->math) < 0){
 					free(v->math);
+					v->math = nil;
 					goto err;
 				}
 				rec->parsed = v->math;
@@ -6232,6 +6278,7 @@
 				v->kern = calloc(1, sizeof(TableKern));
 				if(read_TableKern(o, v->kern) < 0){
 					free(v->kern);
+					v->kern = nil;
 					goto err;
 				}
 				rec->parsed = v->kern;
@@ -6257,7 +6304,8 @@
 				v->loca = calloc(1, sizeof(TableLoca));
 				if(read_TableLoca(o, v->loca) < 0){
 					free(v->loca);
-					goto err;
+					v->loca = nil;
+					break;
 				}
 				rec->parsed = v->loca;
 				rec->print = (void*)print_TableLoca;
@@ -6268,6 +6316,7 @@
 				v->fvar = calloc(1, sizeof(TableFvar));
 				if(read_TableFvar(o, v->fvar) < 0){
 					free(v->fvar);
+					v->fvar = nil;
 					goto err;
 				}
 				rec->parsed = v->fvar;
@@ -6293,6 +6342,7 @@
 				v->hvar = calloc(1, sizeof(TableHVAR));
 				if(read_TableHVAR(o, v->hvar) < 0){
 					free(v->hvar);
+					v->hvar = nil;
 					goto err;
 				}
 				rec->parsed = v->hvar;
@@ -6304,6 +6354,7 @@
 				v->fftm = calloc(1, sizeof(TableFFTM));
 				if(read_TableFFTM(o, v->fftm) < 0){
 					free(v->fftm);
+					v->fftm = nil;
 					goto err;
 				}
 				rec->parsed = v->fftm;
@@ -6315,6 +6366,7 @@
 				v->dsig = calloc(1, sizeof(TableDSIG));
 				if(read_TableDSIG(o, v->dsig) < 0){
 					free(v->dsig);
+					v->dsig = nil;
 					goto err;
 				}
 				rec->parsed = v->dsig;
@@ -6326,6 +6378,7 @@
 				v->stat = calloc(1, sizeof(TableSTAT));
 				if(read_TableSTAT(o, v->stat) < 0){
 					free(v->stat);
+					v->stat = nil;
 					goto err;
 				}
 				rec->parsed = v->stat;
@@ -6337,6 +6390,7 @@
 				v->gasp = calloc(1, sizeof(TableGasp));
 				if(read_TableGasp(o, v->gasp) < 0){
 					free(v->gasp);
+					v->gasp = nil;
 					goto err;
 				}
 				rec->parsed = v->gasp;
@@ -6362,6 +6416,7 @@
 				v->gvar = calloc(1, sizeof(TableGvar));
 				if(read_TableGvar(o, v->gvar) < 0){
 					free(v->gvar);
+					v->gvar = nil;
 					goto err;
 				}
 				rec->parsed = v->gvar;
@@ -6373,6 +6428,7 @@
 				v->avar = calloc(1, sizeof(TableAvar));
 				if(read_TableAvar(o, v->avar) < 0){
 					free(v->avar);
+					v->avar = nil;
 					goto err;
 				}
 				rec->parsed = v->avar;
@@ -6384,6 +6440,7 @@
 				v->os∕2 = calloc(1, sizeof(TableOS∕2));
 				if(read_TableOS∕2(o, v->os∕2) < 0){
 					free(v->os∕2);
+					v->os∕2 = nil;
 					goto err;
 				}
 				rec->parsed = v->os∕2;
--- a/plan9/otf.h
+++ b/plan9/otf.h
@@ -52,6 +52,9 @@
 	int format;
 	int offset;
 	int size;
+	int pitchBits;
+	int ppemX;
+	int ppemY;
 	int numComponents;
 	EbdtComponent *components;
 	u8int image[];
@@ -130,7 +133,7 @@
 Rune otfglyph2rune(Otf *o, int g);
 
 Glyf *otfglyf(Otf *o, int index);
-GlyfImage *otfdrawglyf(Otf *o, Glyf *g, double ppem, double scaleX, double scaleY);
+GlyfImage *otfdrawglyf(Otf *o, Glyf *g, double ppemX, double ppemY);
 
 typedef struct SubHeader SubHeader;
 typedef struct MapGroup MapGroup;
--- a/rast.c
+++ b/rast.c
@@ -548,15 +548,14 @@
 }
 
 static GlyfImage *
-outlineglyf(Otf *o, Glyf *g, double ppem, double scaleX, double scaleY)
+outlineglyf(Otf *o, Glyf *g, double ppemX, double ppemY)
 {
 	int i, j, maxptstotal, maxpts, ngs, w, h, npx, baseline;
+	Sval bb[4], offY, scaleX, scaleY;
 	Glyf *gs[MAXCOMPONENTS] = {nil};
 	ComponentGlyph *cg;
 	SegQ *s₀, *s, *p;
 	GlyfImage *im;
-	Sval bb[4];
-	Sval offY;
 	Spt *pts;
 
 	ngs = 0;
@@ -563,7 +562,7 @@
 	pts = nil;
 	im = nil;
 
-	if(g->simple != nil){
+	if(g->type == GLYPH_SIMPLE){
 		gs[ngs++] = g;
 	}else{
 		for(cg = g->component; cg != nil && ngs < nelem(gs); cg = cg->next, ngs++){
@@ -589,14 +588,14 @@
 */
 
 	pts = calloc(1, maxpts*sizeof(*pts) + maxptstotal/2*sizeof(*s));
-	scaleX *= ppem / o->td.head->unitsPerEm;
-	scaleY *= ppem / o->td.head->unitsPerEm;
+	scaleX = ppemX / o->td.head->unitsPerEm;
+	scaleY = ppemY / o->td.head->unitsPerEm;
 	bb[0] = g->xMin*scaleX;
 	bb[1] = g->yMin*scaleY;
 	bb[2] = g->xMax*scaleX;
 	bb[3] = g->yMax*scaleY;
 	s = s₀ = (SegQ*)(pts + maxpts);
-	cg = g->component;
+	cg = g->type == GLYPH_COMPONENT ? g->component : nil;
 	for(i = 0; i < ngs; i++){
 		Sval dx = 0, dy = 0, gscaleX = scaleX, gscaleY = scaleY;
 		if(cg != nil){
@@ -681,67 +680,129 @@
 	return im;
 }
 
+static u8int
+bitpixel(BitmapGlyph *bg, int x, int y)
+{
+	int i;
+
+	if(bg->bitDepth == 1){
+		if(bg->format == 2 || bg->format == 5){
+			i = y*bg->pitchBits + x;
+			x = i - (i & ~7);
+			return (bg->image[i>>3] & (0x80>>x)) ? 255 : 0;
+		}
+		i = y*bg->pitchBits+x;
+		x &= 7;
+		return (bg->image[i>>3] & (0x80>>x)) ? 255 : 0;
+	}
+	return 0;
+}
+
+static void
+bitblit(Glyf *g, GlyfImage *im, int zoomX, int zoomY, int *bb)
+{
+	int i, x, y, w, h, tmp;
+	BitmapGlyph *bg;
+	u8int *b, *p, v;
+
+	bg = g->bitmap;
+	w = g->xMax - g->xMin;
+	h = g->yMax - g->yMin;
+	b = im->b + (g->yMin - bb[1])*im->w + (g->xMin - bb[0]);
+
+	for(y = 0; y < h; y++){
+		p = b;
+		for(x = 0; x < w; x++){
+			v = bitpixel(bg, x, y);
+#define blend(a,b) (tmp = a+b, tmp > 255 ? 255 : tmp)
+			*b = blend(*b, v);
+			b++;
+			for(i = 1; i < zoomX; i++, b++)
+				*b = blend(b[0], b[-1]);
+#undef blend
+		}
+		for(i = 1; i < zoomY; i++, b += im->w)
+			memcpy(b, p, w*zoomX);
+	}
+}
+
 GlyfImage *
-bitmapglyf(Otf *o, Glyf *g, double ppem, double scaleX, double scaleY)
+bitmapglyf(Otf *o, Glyf *g, double ppemX, double ppemY)
 {
-	int zoomX, zoomY, w, h, x, y, i;
+	int zoomX, zoomY, n, w, h, x, y, i, bb[4], ngs;
+	Glyf *gs[MAXCOMPONENTS], *cg;
+	EbdtComponent *c;
 	BitmapGlyph *bg;
 	GlyfImage *im;
-	u8int *b, *p;
 
 	USED(o);
+	im = nil;
 	bg = g->bitmap;
-	if(bg->numComponents > 0){
-		/* FIXME implement this. components may also be outlines */
-		werrstr("components not implemented");
-		return nil;
-	}
-
-	if((zoomX = scaleX * ppem / 72) < 1)
+	if((zoomX = ppemX/bg->ppemX) < 1)
 		zoomX = 1;
-	if((zoomY = scaleY * ppem / 72) < 1)
+	if((zoomY = ppemY/bg->ppemY) < 1)
 		zoomY = 1;
 
-	w = g->xMax - g->xMin;
-	h = g->yMax - g->yMin;
+	bb[0] = g->xMin;
+	bb[1] = g->yMin;
+	bb[2] = g->xMax;
+	bb[3] = g->yMax;
+	for(ngs = 0; ngs < nelem(gs) && ngs < bg->numComponents;){
+		c = bg->components + ngs;
+		if((cg = gs[ngs] = otfglyf(o, c->glyphID)) == nil)
+			goto err;
+		ngs++;
+		if(cg->type != GLYPH_BITMAP){
+			/* FIXME spec allows outlines here, oh well */
+			werrstr("component glyph not a bitmap");
+			goto err;
+		}
+		if(bb[0] > (x = cg->xMin+c->xOffset))
+			bb[0] = x;
+		if(bb[1] > (y = cg->yMin+c->yOffset))
+			bb[1] = y;
+		if(bb[2] > (x = cg->xMax+c->xOffset))
+			bb[2] = x;
+		if(bb[3] > (y = cg->yMax+c->yOffset))
+			bb[3] = y;
+	}
+
+	w = bb[2] - bb[0];
+	h = bb[3] - bb[1];
 	im = calloc(1, sizeof(*im) + w*zoomX*h*zoomY);
 	im->w = w*zoomX;
 	im->h = h*zoomY;
-	im->baseline = g->yMin * zoomY;
-	b = im->b;
-	if(bg->bitDepth == 1){
-		for(y = 0; y < h; y++){
-			p = b;
-			for(x = w-1; x >= 0; x--){
-				*b++ = (bg->image[(y*w+x)/8] & (1<<(x&7))) ? 0 : 255;
-				for(i = 1; i < zoomX; i++, b++)
-					*b = b[-1];
-			}
-			for(i = 1; i < zoomY; i++, b += im->w)
-				memcpy(b, p, im->w);
-		}
-	}else if(bg->bitDepth == 4){
-		/* FIXME */
-		werrstr("bitDepth 4 not implemented");
-		free(im);
-		im = nil;
-	}else if(bg->bitDepth == 8){
-		/* FIXME */
-		werrstr("bitDepth 8 not implemented");
-		free(im);
-		im = nil;
+	im->baseline = bb[1] * zoomY;
+
+	bitblit(g, im, zoomX, zoomY, bb);
+	for(i = 0; i < ngs; i++)
+		bitblit(gs[i], im, zoomX, zoomY, bb);
+	n = im->w*im->h;
+	for(i = 0; i < n; i++)
+		im->b[i] = 255 - im->b[i];
+
+	for(i = 0; i < ngs; i++){
+		free(gs[i]->bitmap);
+		free(gs[i]);
 	}
 
 	return im;
+err:
+	for(i = 0; i < ngs; i++){
+		free(gs[i]->bitmap);
+		free(gs[i]);
+	}
+	free(im);
+	return nil;
 }
 
 GlyfImage *
-otfdrawglyf(Otf *o, Glyf *g, double ppem, double scaleX, double scaleY)
+otfdrawglyf(Otf *o, Glyf *g, double ppemX, double ppemY)
 {
 	if(g->type == GLYPH_SIMPLE || g->type == GLYPH_COMPONENT)
-		return outlineglyf(o, g, ppem, scaleX, scaleY);
+		return outlineglyf(o, g, ppemX, ppemY);
 	if(g->type == GLYPH_BITMAP)
-		return bitmapglyf(o, g, ppem, scaleX, scaleY);
+		return bitmapglyf(o, g, ppemX, ppemY);
 	werrstr("empty glyph");
 	return nil;
 }
--- a/test.h
+++ b/test.h
@@ -10,14 +10,14 @@
 	f->print(f->aux, " -m: print out glyph ids or render them all as a map (with -p)\n");
 	f->print(f->aux, " -R: ignore glyphs that do not represent valid runes\n");
 	f->print(f->aux, " -H: highlight a specific glyph in the map by its ID\n");
-	f->print(f->aux, " -x: horizontal scale factor\n");
-	f->print(f->aux, " -y: vertical scale factor\n");
+	f->print(f->aux, " -x: horizontal ppem\n");
+	f->print(f->aux, " -y: vertical ppem\n");
 	f->print(f->aux, "Specifying -m more than once adds guide lines\n");
 	f->print(f->aux, "Specifying -m more than twice makes empty pixels filled out\n");
 }
 
 static int gind = -1, map, highlight = -1, runesonly;
-static double ppem, scaleX = 1, scaleY = 1;
+static double ppemX, ppemY;
 static Rune rune = NoRune;
 static char *gtypes[] = {
 	[GLYPH_EMPTY] = "empty",
@@ -147,7 +147,7 @@
 		goto glypherr;
 	}
 	if(map && gind < 0){
-		im = ppem > 0 ? calloc(n, sizeof(*im)) : nil;
+		im = ppemX > 0 ? calloc(n, sizeof(*im)) : nil;
 		for(i = 0; i < n; i++){
 			if(runesonly && otfglyph2rune(o, i) == NoRune)
 				continue;
@@ -157,16 +157,18 @@
 				werrstr("glyph %d: %r", gind);
 				return -1;
 			}
-			if(ppem > 0 && g->type != GLYPH_EMPTY){
-				if((im[i] = otfdrawglyf(o, g, ppem, scaleX, scaleY)) == nil)
+			if(ppemX > 0 && g->type != GLYPH_EMPTY){
+				if((im[i] = otfdrawglyf(o, g, ppemX, ppemY)) == nil){
+					gind = i;
 					goto glypherr;
-			}else if(ppem <= 0){
+				}
+			}else if(ppemX <= 0){
 				out->print(out->aux, "%d (%s):\n", i, gtypes[g->type]);
 				print_Glyf(out, indentΔ, o, g);
 			}
 			free(g);
 		}
-		if(ppem > 0){
+		if(ppemX > 0){
 			if(out->write != otfdiscard && dumpmap(out, im, n) != 0)
 				return -1;
 			for(i = 0; i < n; i++)
@@ -178,9 +180,9 @@
 	}else{
 		if((g = otfglyf(o, gind)) == nil){
 			goto glypherr;
-		}else if(ppem > 0){
+		}else if(ppemX > 0){
 			GlyfImage *im;
-			if((im = otfdrawglyf(o, g, ppem, scaleX, scaleY)) == nil)
+			if((im = otfdrawglyf(o, g, ppemX, ppemY)) == nil)
 				goto glypherr;
 			if(out->write != otfdiscard && dumpmap(out, &im, 1) != 0)
 				return -1;
@@ -211,7 +213,7 @@
 		map++; \
 		break; \
 	case 'p': \
-		ppem = strtod(EARGF(usage(&out)), nil); \
+		ppemX = ppemY = strtod(EARGF(usage(&out)), nil); \
 		break; \
 	case 'r': { \
 		char *s = EARGF(usage(&out)); \
@@ -228,13 +230,17 @@
 		runesonly++; \
 		break; \
 	case 'x': \
-		scaleX = strtod(EARGF(usage(&out)), nil); \
+		ppemX = strtod(EARGF(usage(&out)), nil); \
 		break; \
 	case 'y': \
-		scaleY = strtod(EARGF(usage(&out)), nil); \
+		ppemY = strtod(EARGF(usage(&out)), nil); \
 		break; \
 	default: \
 		usage(&out); \
 	}ARGEND \
 	if(argc != 1) \
-		usage(&out);
+		usage(&out); \
+	if(ppemX == 0 && ppemY != 0) \
+		ppemX = ppemY; \
+	if(ppemY == 0 && ppemX != 0) \
+		ppemY = ppemX;