small bug in ttf reading
[swftools.git] / lib / ttf.c
index c17b631..c1fd06d 100644 (file)
--- a/lib/ttf.c
+++ b/lib/ttf.c
 #include <stdio.h>
 #include <stdlib.h>
 #include <memory.h>
+#include <assert.h>
 #include "log.h"
 #include "os.h"
+#include "q.h"
 #include "mem.h"
 #include "ttf.h"
 
 #define TAG_NAME 0x6e616d65
 #define TAG_POST 0x706f7374
 #define TAG_CFF  0x43464620 //required for opentype
+#define TAG_CVT  0x63767420 
+#define TAG_FPGM 0x6670676d 
+#define TAG_GASP 0x67617370 
+#define TAG_PREP 0x70726570 
 
-/* TODO:
-    fpgm - assembly instructions
-    prep - assembly instructions
-    cvt - constant value table
-    gasp - gridfitting procedure
-*/
 
 static U32 checksum_block(U8*_data, int len)
 {
@@ -116,6 +116,10 @@ static void readBlock(memreader_t*r, void*dest, int len)
        r->pos += len;
     }
 }
+static void reader_reset(memreader_t*r)
+{
+    r->pos;
+}
 #define INIT_READ(r,data,length,pos) memreader_t r = {(data),(pos),(length)};
 
 static void expand(ttf_table_t*w, int newsize)
@@ -138,6 +142,13 @@ static inline void writeU16(ttf_table_t*w, unsigned short v)
     w->data[w->len++] = v>>8;
     w->data[w->len++] = v;
 }
+static inline void writeU16_LE(ttf_table_t*w, unsigned short v)
+{
+    if(w->memsize<w->len+2)
+       expand(w, w->len+2);
+    w->data[w->len++] = v;
+    w->data[w->len++] = v>>8;
+}
 #define writeS16 writeU16
 static inline void writeU32(ttf_table_t*w, unsigned long v)
 {
@@ -148,6 +159,15 @@ static inline void writeU32(ttf_table_t*w, unsigned long v)
     w->data[w->len++] = v>>8;
     w->data[w->len++] = v;
 }
+static inline void writeU32_LE(ttf_table_t*w, unsigned long v)
+{
+    if(w->memsize<w->len+4)
+       expand(w, w->len+4);
+    w->data[w->len++] = v;
+    w->data[w->len++] = v>>8;
+    w->data[w->len++] = v>>16;
+    w->data[w->len++] = v>>24;
+}
 static inline void writeBlock(ttf_table_t*w, void*data, int len)
 {
     if(w->memsize<w->len+len)
@@ -178,7 +198,7 @@ ttf_table_t*ttf_addtable(ttf_t*ttf, U32 id)
        before->len = 0;
        return before;
     }
-    
+
     if(!after) {
        t->next = ttf->tables;
        ttf->tables = t;
@@ -195,7 +215,7 @@ ttf_table_t*ttf_find_table(ttf_t*ttf, U32 id)
 {
     ttf_table_t*table = ttf->tables;
     while(table) {
-       if(table->id == id) 
+       if(table->id == id)
            return table;
        table = table->next;
     }
@@ -206,9 +226,9 @@ void ttf_table_delete(ttf_t*ttf, ttf_table_t*table)
     if(ttf && ttf->tables == table) {
        ttf->tables = table->next;
     }
-    if(table->prev) 
+    if(table->prev)
        table->prev->next = table->next;
-    if(table->next) 
+    if(table->next)
        table->next->prev = table->prev;
     free(table->data);
     free(table);
@@ -268,10 +288,10 @@ static table_head_t*head_new(ttf_t*ttf)
        head->xmax = ttf->glyphs[0].xmax;
        head->ymax = ttf->glyphs[0].ymax;
        for(t=1;t<ttf->num_glyphs;t++) {
-           if(ttf->glyphs[0].xmin < head->xmin) head->xmin = ttf->glyphs[0].xmin;
-           if(ttf->glyphs[0].ymin < head->ymin) head->ymin = ttf->glyphs[0].ymin;
-           if(ttf->glyphs[0].xmax > head->xmax) head->xmax = ttf->glyphs[0].xmax;
-           if(ttf->glyphs[0].ymax > head->ymax) head->ymax = ttf->glyphs[0].ymax;
+           if(ttf->glyphs[t].xmin < head->xmin) head->xmin = ttf->glyphs[t].xmin;
+           if(ttf->glyphs[t].ymin < head->ymin) head->ymin = ttf->glyphs[t].ymin;
+           if(ttf->glyphs[t].xmax > head->xmax) head->xmax = ttf->glyphs[t].xmax;
+           if(ttf->glyphs[t].ymax > head->ymax) head->ymax = ttf->glyphs[t].ymax;
        }
     }
     head->macStyle = 0;
@@ -283,12 +303,12 @@ static int head_parse(ttf_t*ttf, memreader_t*r)
 {
     ttf->head = rfx_calloc(sizeof(table_head_t));
     U32 version = readU32(r);
-    if(version!=VERSION_1_0) 
+    if(version!=VERSION_1_0)
        msg("<warning> Font HEAD has unknown version %08x", version);
     U32 revision = readU32(r);
     U32 checksum2 = readU32(r);
     U32 magic = readU32(r);
-    if(magic!=0x5f0f3cf5) 
+    if(magic!=0x5f0f3cf5)
        msg("<warning> Font HEAD has unknown magic number %08x", magic);
     ttf->head->flags = readU16(r);
     ttf->head->units_per_em = readU16(r);
@@ -367,7 +387,7 @@ static table_os2_t*os2_new(ttf_t*ttf)
     /* that's what everybody seems to fill in */
     os2->usWeightClass = 400;
     os2->usWidthClass = 5;
-   
+
     if(ttf->head) {
        int advance = (ttf->head->xmax - ttf->head->xmin)/2;
        int height = (ttf->head->xmax - ttf->head->xmin);
@@ -383,8 +403,8 @@ static table_os2_t*os2_new(ttf_t*ttf)
        os2->ySuperscriptYOffset = (ttf->head->ymax - ttf->head->ymin)/2;
        os2->yStrikeoutSize = ttf->head->units_per_em / 10;
        os2->yStrikeoutPosition = ymid;
-       os2->usWinAscent = ttf->head->ymax;
-       os2->usWinDescent = ttf->head->ymin<0?0:ttf->head->ymin;
+       os2->usWinAscent = ttf->ascent;
+       os2->usWinDescent = ttf->descent>0?0:-ttf->descent;
        os2->sxHeight = ymid;
        os2->sCapHeight = height*2/3;
     }
@@ -476,7 +496,7 @@ static table_os2_t*os2_parse(memreader_t*r)
     os2->usDefaultChar = readU16(r);
     os2->usBreakChar = readU16(r);
     os2->usMaxContext = readU16(r);
-    
+
     if(r->pos < r->size) {
        msg("<warning> Leftover bytes (%d) in OS2 tag", r->size - r->pos);
     }
@@ -597,8 +617,9 @@ static table_maxp_t*maxp_new(ttf_t*ttf)
 {
     table_maxp_t*maxp = rfx_calloc(sizeof(table_maxp_t));
     int t;
+    maxp->maxContours=1;
     if(ttf->num_glyphs) {
-       int max = 0;
+       int max = 1;
        for(t=0;t<ttf->num_glyphs;t++) {
            if(ttf->glyphs[t].num_points>max)
                max = ttf->glyphs[t].num_points;
@@ -617,13 +638,14 @@ static table_maxp_t*maxp_new(ttf_t*ttf)
        maxp->maxComponentPoints = 0;
        maxp->maxComponentContours = 0;
     }
+    maxp->maxZones = 2; // we don't use the Z0 zone
     return maxp;
 }
 static table_maxp_t* maxp_parse(ttf_t*ttf, memreader_t*r)
 {
     U32 version = readU32(r);
     ttf->num_glyphs = readU16(r);
-    /* according to freetype, older fonts (version<0x10000) 
+    /* according to freetype, older fonts (version<0x10000)
        apparently only contain the number of glyphs. this is
        rather rare, though. */
     if(version<0x10000 && r->size==6) return 0;
@@ -705,15 +727,15 @@ static table_hea_t*hea_new(ttf_t*ttf)
        for(t=0;t<ttf->num_glyphs;t++) {
            if(ttf->glyphs[t].advance > hea->advanceWidthMax)
                hea->advanceWidthMax = ttf->glyphs[t].advance;
-           if(ttf->glyphs[t].xmin < ttf->hea->minLeftSideBearing)
-               ttf->hea->minLeftSideBearing = ttf->glyphs[t].xmin;
-           if(ttf->glyphs[t].xmax < ttf->hea->minRightSideBearing)
-               ttf->hea->minRightSideBearing = ttf->glyphs[t].xmax;
+           if(ttf->glyphs[t].bearing < hea->minLeftSideBearing)
+               hea->minLeftSideBearing = ttf->glyphs[t].bearing;
+           if(ttf->glyphs[t].xmax < hea->minRightSideBearing)
+               hea->minRightSideBearing = ttf->glyphs[t].xmax;
            int width = ttf->glyphs[t].xmax - ttf->glyphs[t].xmin;
            if(width > hea->xMaxExtent)
                hea->xMaxExtent = width;
        }
-       /* TODO: caret */
+       hea->caretSlopeRise = 1;
     }
     return hea;
 }
@@ -807,7 +829,7 @@ static void mtx_parse(memreader_t*r, ttf_t*ttf, int num_advances)
        rest = ttf->num_glyphs-num_advances;
     }
     for(t=0;t<rest;t++) {
-       ttf->glyphs[t].advance = old_advance; 
+       ttf->glyphs[t].advance = old_advance;
        ttf->glyphs[t].bearing = readS16(r);
     }
 }
@@ -828,10 +850,10 @@ static int mtx_write(ttf_t*ttf, ttf_table_t*w)
     int t;
     for(t=0;t<num_advances;t++) {
        writeU16(w, ttf->glyphs[t].advance);
-       writeU16(w, ttf->glyphs[t].bearing);
+       writeS16(w, ttf->glyphs[t].bearing);
     }
     for(;t<ttf->num_glyphs;t++) {
-       writeU16(w, ttf->glyphs[t].bearing);
+       writeS16(w, ttf->glyphs[t].bearing);
     }
     return num_advances;
 }
@@ -927,15 +949,15 @@ static int parse_simple_glyph(ttf_t*ttf, memreader_t*r, int num_contours, int gl
        glyph->code_size = code_len;
     }
 
-    if(!endpoints) 
+    if(!endpoints)
        return 1;
 
-    /*msg("<notice> TTF Glyph %d) code_size=%d num_contours=%d glyph->num_points=%d %d/%d/%d/%d", 
+    /*msg("<notice> TTF Glyph %d) code_size=%d num_contours=%d glyph->num_points=%d %d/%d/%d/%d",
            glyphnr, code_len, num_contours, glyph->num_points,
            xmin, ymin, xmax, ymax);*/
     INIT_READ(fx, r->mem, r->size, r->pos);
     INIT_READ(fy, r->mem, r->size, r->pos);
-    
+
     glyph->num_points = endpoints[num_contours-1] + 1;
     glyph->points = rfx_calloc(sizeof(ttfpoint_t)*glyph->num_points);
 
@@ -951,7 +973,7 @@ static int parse_simple_glyph(ttf_t*ttf, memreader_t*r, int num_contours, int gl
            return 0;
        }
        int count = 1;
-       if(flag & 0x08) 
+       if(flag & 0x08)
            count += readU8(r);
        if(count+num>glyph->num_points) {
            msg("<warning> Bad count (%d) in glyph (%d) (at pos %d)", count, glyphnr, num);
@@ -980,7 +1002,7 @@ static int parse_simple_glyph(ttf_t*ttf, memreader_t*r, int num_contours, int gl
            if((flag&0x12) == 0x12) x += readU8(r);
            else if((flag&0x12) == 0x02) x -= readU8(r);
            else if((flag&0x12) == 0x00) x += readS16(r);
-           
+
            glyph->points[num].x = x;
            U8 f = flag&GLYPH_ON_CURVE;
            if(is_start) f|=GLYPH_CONTOUR_START;
@@ -990,7 +1012,7 @@ static int parse_simple_glyph(ttf_t*ttf, memreader_t*r, int num_contours, int gl
            is_start = is_end;
        } while(--count);
     }
-    
+
     /* parse flag array (3rd pass) and y coordinates */
     num=0;
     int y = 0;
@@ -1018,7 +1040,7 @@ static void glyf_parse(memreader_t*rr, ttf_t*ttf, U32*loca)
        if(loca[t]==loca[t+1] || loca[t]==r.size)
            continue; //empty glyph
        if(r.pos+10>r.size) {
-           msg("<warning> Truncated glyph entry %d/%d (or bad loca entry %d/%d, next loca: %d)", 
+           msg("<warning> Truncated glyph entry %d/%d (or bad loca entry %d/%d, next loca: %d)",
                    t, ttf->num_glyphs, loca[t], r.size, loca[t+1]);
            break;
        }
@@ -1027,7 +1049,7 @@ static void glyf_parse(memreader_t*rr, ttf_t*ttf, U32*loca)
        ttf->glyphs[t].ymin = readS16(&r);
        ttf->glyphs[t].xmax = readS16(&r);
        ttf->glyphs[t].ymax = readS16(&r);
-       
+
        if(num_contours<0) {
            if(warn_about_compound_glyphs)
                msg("<error> Compound glyphs not supported yet");
@@ -1047,7 +1069,7 @@ void write_simple_glyph(ttf_table_t*w, ttfglyph_t*g)
        if(g->points[s].flags&GLYPH_CONTOUR_END)
            writeU16(w, s);
     }
-    
+
     /* bytecode */
     writeU16(w, g->code_size);
     if(g->code_size)
@@ -1148,7 +1170,7 @@ U32* glyf_write(ttf_t* ttf, ttf_table_t*w)
        writeS16(w, g->ymin);
        writeS16(w, g->xmax);
        writeS16(w, g->ymax);
-       
+
        if(!num_contours) {
            /* some ttf parsers can't deal with zero contours, so in the case
               of an empty glyph, write a single point (0,0) */
@@ -1181,13 +1203,13 @@ void glyf_dump(ttf_t* ttf)
            printf("%d/%d/0x%02x", g->points[s].x, g->points[s].y, g->points[s].flags);
        }
        printf(")\n");
-       if(g->code_size) 
+       if(g->code_size)
            hexdump(g->code, g->code_size, "  ");
     }
 }
 void glyf_delete(ttf_t* ttf)
 {
-    if(!ttf->glyphs) 
+    if(!ttf->glyphs)
        return;
     int t;
     for(t=0;t<ttf->num_glyphs;t++) {
@@ -1219,6 +1241,7 @@ void cmap_parse(memreader_t*r, ttf_t*ttf)
     readU16(r); // version (0)
     int num_subtables = readU16(r);
     int t;
+    char warn=1;
     if(r->pos+num_subtables*8 > r->size) {
        msg("<warning> CMap overflow");
        num_subtables = (r->size-r->pos)/8;
@@ -1237,7 +1260,7 @@ void cmap_parse(memreader_t*r, ttf_t*ttf)
                         platform==3 && encoding == 1 ||
                         platform==3 && encoding == 10;
 
-       if(!is_unicode) 
+       if(!is_unicode)
            continue;
 
        INIT_READ(t, r->mem, r->size, offset);
@@ -1262,7 +1285,7 @@ void cmap_parse(memreader_t*r, ttf_t*ttf)
                ttf->unicode[s] = readU8(&t);
            }
        } else if(format == 4) {
-           U16 segment_count = readU16(&t); 
+           U16 segment_count = readU16(&t);
            if(segment_count&1) {
                msg("<error> Bad segmentx2 count %d", segment_count);
                continue;
@@ -1276,6 +1299,7 @@ void cmap_parse(memreader_t*r, ttf_t*ttf)
            INIT_READ(r_delta, t.mem, t.size, t.pos+2+segment_count*4);
            INIT_READ(r_range, t.mem, t.size, t.pos+2+segment_count*6);
            int glyphmap_start = t.pos+2+segment_count*8;
+           int glyphmap_size = t.size - glyphmap_start;
            int s;
            for(s=0;s<segment_count;s++) {
                U16 start = readU16(&r_start);
@@ -1283,8 +1307,8 @@ void cmap_parse(memreader_t*r, ttf_t*ttf)
                U16 delta = readU16(&r_delta);
                U16 range = readU16(&r_range);
                if(start==0xffff && end==0xffff && delta==1) {
-                   /* this is a common (maybe even required) occurence in fonts 
-                      which explicitly map "unicode undefined" (0xffff) to 
+                   /* this is a common (maybe even required) occurence in fonts
+                      which explicitly map "unicode undefined" (0xffff) to
                       "glyph undefined" (0).
                       We don't want to blow our unicode table up to 65536 just
                       because of this, so ignore this entry.
@@ -1298,7 +1322,12 @@ void cmap_parse(memreader_t*r, ttf_t*ttf)
                        ttf->unicode[u] = (u + delta) & 0xffff;
                    }
                } else {
-                   INIT_READ(g, t.mem, t.size, r_range.pos-2+range);
+                   int pos = r_range.pos-2+range;
+                   if(warn && pos+end-start+1 > t.size) {
+                       msg("<warning> glyphmap index out of bounds (%d-%d/%d)", pos, pos+end-start, t.size);
+                       warn=0;
+                   }
+                   INIT_READ(g, t.mem, t.size, pos);
                    for(u=start;u<=end;u++) {
                        ttf->unicode[u] = readU16(&g);
                    }
@@ -1315,7 +1344,7 @@ static int segment_size(unicode_t*unicode, int pos, int size)
        if(!unicode[s])
            count++;
        if(count>4) {
-           /* a segment costs us 8 bytes, so for more than 4 consecutive 
+           /* a segment costs us 8 bytes, so for more than 4 consecutive
               zero entries (16 bit each) in the glyph index array,
               it pays off to start a new segment */
            break;
@@ -1363,6 +1392,8 @@ void cmap_write(ttf_t* ttf, ttf_table_t*w)
 
     num_segments++; // account for 0xffff mapping
 
+    int glyphmap_start = w->len+2+num_segments*8;
+
     int t;
     int end_pos = w->len;
     for(t=0;t<num_segments;t++) {writeU16(w, 0);} //end array
@@ -1373,9 +1404,29 @@ void cmap_write(ttf_t* ttf, ttf_table_t*w)
     for(t=0;t<num_segments;t++) {writeU16(w, 0);} //delta array
     int range_pos = w->len;
     for(t=0;t<num_segments;t++) {writeU16(w, 0);} //range array
-    
-    w->data[num_segments_pos]=(num_segments*2)>>8;
-    w->data[num_segments_pos+1]=(num_segments*2);
+
+    /* backpatch number of segments */
+    w->data[num_segments_pos++]=(num_segments*2)>>8;
+    w->data[num_segments_pos++]=(num_segments*2);
+    /* backpatch search range */
+    int tmp = num_segments;
+    int search_range = 0;
+    while(tmp) {
+       search_range = tmp;
+       tmp = tmp&(tmp-1);
+    }
+    w->data[num_segments_pos++]=(search_range*2)>>8;
+    w->data[num_segments_pos++]=(search_range*2);
+    /* backpatch entry selector */
+    int entry_selector = 0;
+    tmp = search_range;
+    while(tmp>1) {tmp>>=1;entry_selector++;}
+    w->data[num_segments_pos++]=entry_selector>>8;
+    w->data[num_segments_pos++]=entry_selector;
+    /* backpatch range shift */
+    int range_shift = num_segments*2 - search_range*2;
+    w->data[num_segments_pos++]=range_shift>>8;
+    w->data[num_segments_pos++]=range_shift;
 
     pos=0;
     num_segments = 0;
@@ -1393,7 +1444,7 @@ void cmap_write(ttf_t* ttf, ttf_table_t*w)
        U16 delta = ttf->unicode[pos]-pos;
        char do_delta=1;
        for(s=pos+1;s<=end;s++) {
-           U16 delta2 = ttf->unicode[pos]-pos;
+           U16 delta2 = ttf->unicode[s]-s;
            if(delta2!=delta) {
                do_delta=0;
                break;
@@ -1404,7 +1455,7 @@ void cmap_write(ttf_t* ttf, ttf_table_t*w)
            range = 0;
        } else {
            delta = 0;
-           range = w->len - range_pos+num_segments*2;
+           range = w->len - range_pos;
            for(s=pos;s<=end;s++) {
                writeU16(w, ttf->unicode[s]);
            }
@@ -1439,6 +1490,330 @@ void cmap_delete(ttf_t*ttf)
     }
     ttf->unicode_size=0;
 }
+static char*readString(memreader_t*r, int len)
+{
+    char*s = malloc(len+1);
+    readBlock(r, s, len);
+    s[len] = 0;
+    return s;
+}
+void name_parse(memreader_t*r, ttf_t*ttf)
+{
+    U16 format = readU16(r);
+    U16 count = readU16(r);
+    U16 offset = readU16(r);
+
+    int t;
+    for(t=0;t<count;t++) {
+       U16 platform = readU16(r);
+       U16 encoding = readU16(r);
+       U16 language = readU16(r);
+       U16 name_id = readU16(r);
+       U16 len = readU16(r);
+       U16 offset_2 = readU16(r);
+
+       char ** read_name = 0;
+
+       INIT_READ(ss, r->mem, r->size, offset+offset_2);
+       if(!(platform==0 || (platform==1 && encoding==0)))
+               continue;
+
+       INIT_READ(s, r->mem, r->size, offset+offset_2);
+
+       switch (name_id) {
+           case 1: read_name = &ttf->family_name; break;
+           case 2: read_name = &ttf->subfamily_name; break;
+           case 3: read_name = &ttf->font_uid; break;
+           case 4: read_name = &ttf->full_name; break;
+           case 5: read_name = &ttf->version_string; break;
+           case 6: read_name = &ttf->postscript_name; break;
+           default: read_name = 0;
+       }
+
+       if (read_name) {
+           if (*read_name) free(*read_name);
+           *read_name = readString(&s, len);
+       }
+    }
+}
+void name_write(ttf_t*ttf, ttf_table_t*table)
+{
+    char*strings[6] = {ttf->family_name, ttf->subfamily_name, ttf->font_uid, ttf->full_name, ttf->version_string, ttf->postscript_name};
+    int codes[6] = {1,2,3,4,5,6};
+
+    writeU16(table, 0); //format
+    int count = 0;
+    int t;
+    int nr = sizeof(strings)/sizeof(strings[0]);
+
+    for(t=0;t<nr;t++) {
+       if(strings[t])
+           count+=2;
+    }
+    writeU16(table, count); //count
+
+    int offset_pos = table->len;
+    writeU16(table, 0); //offset (will be filled in later)
+
+    /* Windows expects the name table to be sorted by platform/encoding/language/name_id */
+    int offset = 0;
+    for(t=0;t<nr;t++) {
+       if(strings[t]) {
+           writeU16(table, 1); //platform id (mac)
+           writeU16(table, 0); //encoding id (latin-1)
+           writeU16(table, 0); //language (english)
+           writeU16(table, codes[t]);
+           int len = strlen(strings[t]);
+           writeU16(table, len);
+           writeU16(table, offset);
+           offset += len;
+       }
+    }
+    for(t=0;t<nr;t++) {
+       if(strings[t]) {
+           writeU16(table, 3); //platform id (windows)
+           writeU16(table, 1); //encoding id (ucs-2)
+           writeU16(table, 0x409); //language (US)
+           writeU16(table, codes[t]);
+           int len2 = strlen(strings[t]) * 2;
+           writeU16(table, len2);
+           writeU16(table, offset);
+           offset += len2;
+       }
+    }
+
+    table->data[offset_pos] = table->len>>8;
+    table->data[offset_pos+1] = table->len;
+
+    for(t=0;t<nr;t++) {
+       if(strings[t]) {
+           int len = strlen(strings[t]);
+           writeBlock(table, strings[t], len);
+       }
+    }
+    for(t=0;t<nr;t++) {
+       if(strings[t]) {
+           int s;
+           int len = strlen(strings[t]);
+           for(s=0;s<len;s++) {
+               writeU8(table, 0);
+               writeU8(table, strings[t][s]);
+           }
+       }
+    }
+}
+void name_delete(ttf_t*ttf)
+{
+    if(ttf->full_name) {
+       free(ttf->full_name);
+       ttf->full_name=0;
+    }
+    if(ttf->family_name) {
+       free(ttf->family_name);
+       ttf->family_name=0;
+    }
+    if(ttf->subfamily_name) {
+       free(ttf->subfamily_name);
+       ttf->subfamily_name=0;
+    }
+    if(ttf->version_string) {
+       free(ttf->version_string);
+       ttf->version_string=0;
+    }
+    if(ttf->font_uid) {
+       free(ttf->font_uid);
+       ttf->font_uid=0;
+    }
+    if(ttf->postscript_name) {
+       free(ttf->postscript_name);
+       ttf->postscript_name=0;
+    }
+}
+
+static table_post_t*post_new(ttf_t*ttf)
+{
+    table_post_t*post = rfx_calloc(sizeof(table_post_t));
+    return post;
+}
+void post_parse(memreader_t*r, ttf_t*ttf)
+{
+    table_post_t*post = ttf->post = rfx_calloc(sizeof(table_post_t));
+    U32 format = readU32(r);
+    post->italic_angle = readU32(r);
+    post->underline_position = readU16(r);
+    post->underline_thickness = readU16(r);
+    U16 is_monospaced = readU32(r);
+    readU32(r); // min mem 42
+    readU32(r);
+    readU32(r); // min mem 1
+    readU32(r);
+}
+void post_write(ttf_t*ttf, ttf_table_t*table)
+{
+    table_post_t*post = ttf->post;
+    writeU32(table, 0x00030000);
+    writeU32(table, post->italic_angle);
+    writeU16(table, post->underline_position);
+    writeU16(table, post->underline_thickness);
+    writeU32(table, 0); //is monospaced TODO
+    writeU32(table, 0); //min mem 42
+    writeU32(table, 0);
+    writeU32(table, 0); //min mem 1
+    writeU32(table, 0);
+}
+void post_delete(ttf_t*ttf)
+{
+    if(ttf->post) {
+       free(ttf->post);
+       ttf->post = 0;
+    }
+}
+
+void cvt_parse(memreader_t*r, ttf_t*ttf)
+{
+    table_cvt_t*cvt = ttf->cvt = rfx_calloc(sizeof(table_cvt_t));
+    cvt->num = r->size/2;
+    cvt->values = malloc(cvt->num*sizeof(S16));
+    int t;
+    for(t=0;t<cvt->num;t++) {
+       cvt->values[t] = readS16(r);
+    }
+}
+void cvt_write(ttf_t*ttf, ttf_table_t*table)
+{
+    table_cvt_t*cvt = ttf->cvt;
+    int t;
+    for(t=0;t<cvt->num;t++) {
+       writeS16(table, cvt->values[t]);
+    }
+}
+void cvt_delete(ttf_t*ttf)
+{
+    if(ttf->cvt) {
+       if(ttf->cvt->values) 
+           free(ttf->cvt->values);
+       free(ttf->cvt);
+       ttf->cvt = 0;
+    }
+}
+
+static table_gasp_t*gasp_new(ttf_t*ttf)
+{
+    table_gasp_t*gasp = rfx_calloc(sizeof(table_gasp_t));
+    gasp->num = 1;
+    gasp->records = rfx_calloc(sizeof(gasp->records[0])*gasp->num);
+
+    gasp->records[0].size = 65535;
+    gasp->records[0].behaviour = 15; //gridfit+grayscale rendering
+    return gasp;
+}
+void gasp_parse(memreader_t*r, ttf_t*ttf)
+{
+    table_gasp_t*gasp = ttf->gasp = rfx_calloc(sizeof(table_gasp_t));
+    readU16(r); //version
+    int num = readU16(r);
+    int t;
+    if(!num) return;
+    gasp->records = malloc(sizeof(gasp->records[0])*num);
+    for(t=0;t<num;t++) {
+       gasp->records[t].size = readU16(r);
+       gasp->records[t].behaviour = readU16(r);
+    }
+}
+
+#define GASP_SYMMETRIC_GRIDFIT 0x0008
+#define GASP_SYMMETRIC_SMOOTHING 0x0004
+#define GASP_DOGRAY 0x0002
+#define GASP_GRIDFIT 0x0001
+
+void gasp_write(ttf_t*ttf, ttf_table_t*table)
+{
+    table_gasp_t*gasp = ttf->gasp;
+    int version = 0;
+    int t;
+    for(t=0;t<gasp->num;t++) {
+       if(gasp->records[t].behaviour & ~(GASP_GRIDFIT | GASP_DOGRAY)) {
+           version = 1;
+       }
+    }
+    writeU16(table, version);
+    writeU16(table, gasp->num);
+    for(t=0;t<gasp->num;t++) {
+       writeU16(table, gasp->records[t].size);
+       writeU16(table, gasp->records[t].behaviour);
+    }
+}
+void gasp_delete(ttf_t*ttf)
+{
+    if(ttf->gasp) {
+       if(ttf->gasp->records)
+           free(ttf->gasp->records);
+       free(ttf->gasp);
+       ttf->gasp = 0;
+    }
+}
+
+table_code_t*prep_new(ttf_t*ttf)
+{
+    table_code_t*prep = ttf->prep = rfx_calloc(sizeof(table_code_t));
+    ttf_table_t*t = ttf_table_new(0);
+    writeU8(t,0xb8);writeU16(t,0x1ff); // pushword(0x1ff)
+    writeU8(t,0x85); //scanctrl (always do dropout, for all sizes)
+    writeU8(t,0xb0);writeU8(t,1); // pushbyte(1)
+    writeU8(t,0x8d); //scantype (simple dropout control w/o stubs)
+    writeU8(t,0xb0);writeU8(t,5); // pushbyte(5)
+    writeU8(t,0x8d); //scantype (for windows) smart dropout control w/o stubs
+    prep->code = t->data;
+    prep->size = t->len;
+    free(t);
+    return prep;
+
+}
+void fpgm_parse(memreader_t*r, ttf_t*ttf)
+{
+    table_code_t*fpgm = ttf->fpgm  = rfx_calloc(sizeof(table_code_t));
+    if(!r->size) return;
+    fpgm->size = r->size;
+    fpgm->code = malloc(r->size);
+    readBlock(r, fpgm->code, r->size);
+}
+void fpgm_write(ttf_t*ttf, ttf_table_t*table)
+{
+    table_code_t*code = ttf->fpgm;
+    writeBlock(table, code->code, code->size);
+}
+void fpgm_delete(ttf_t*ttf)
+{
+    if(ttf->fpgm) {
+       if(ttf->fpgm->code)
+           free(ttf->fpgm->code);
+       free(ttf->fpgm);
+       ttf->fpgm = 0;
+    }
+}
+
+void prep_parse(memreader_t*r, ttf_t*ttf)
+{
+    table_code_t*prep = ttf->prep  = rfx_calloc(sizeof(table_code_t));
+    if(!r->size) return;
+    prep->size = r->size;
+    prep->code = malloc(r->size);
+    readBlock(r, prep->code, r->size);
+}
+void prep_write(ttf_t*ttf, ttf_table_t*table)
+{
+    table_code_t*code = ttf->prep;
+    writeBlock(table, code->code, code->size);
+}
+void prep_delete(ttf_t*ttf)
+{
+    if(ttf->prep) {
+       if(ttf->prep->code)
+           free(ttf->prep->code);
+       free(ttf->prep);
+       ttf->prep = 0;
+    }
+}
 
 static int ttf_parse_tables(ttf_t*ttf)
 {
@@ -1452,7 +1827,7 @@ static int ttf_parse_tables(ttf_t*ttf)
     INIT_READ(m, table->data, table->len, 0);
     int loc_index = head_parse(ttf, &m);
     ttf_table_delete(ttf, table);
-    
+
     table = ttf_find_table(ttf, TAG_MAXP);
     if(!table) {
        msg("<error> Font has no maxp table");
@@ -1461,7 +1836,7 @@ static int ttf_parse_tables(ttf_t*ttf)
     INIT_READ(m2, table->data, table->len, 0);
     ttf->maxp = maxp_parse(ttf, &m2);
     ttf_table_delete(ttf, table);
-    
+
     if(!ttf->num_glyphs) {
        msg("<error> Invalid number of characters");
        return 0;
@@ -1475,12 +1850,13 @@ static int ttf_parse_tables(ttf_t*ttf)
         ttf_table_delete(ttf, table);
     }
 
+
     table = ttf_find_table(ttf, TAG_HHEA);
     if(table) {
        INIT_READ(m, table->data, table->len, 0);
        int num_advances = hea_parse(&m, ttf);
         ttf_table_delete(ttf, table);
-       
+
        table = ttf_find_table(ttf, TAG_HMTX);
        if(table) {
            INIT_READ(m, table->data, table->len, 0);
@@ -1518,7 +1894,7 @@ static int ttf_parse_tables(ttf_t*ttf)
        }
        free(loca);
     }
-    
+
     table = ttf_find_table(ttf, TAG_CMAP);
     if(table) {
        INIT_READ(m, table->data, table->len, 0);
@@ -1526,47 +1902,135 @@ static int ttf_parse_tables(ttf_t*ttf)
        ttf_table_delete(ttf, table);
     }
 
+    table = ttf_find_table(ttf, TAG_POST);
+    if(table) {
+       INIT_READ(m, table->data, table->len, 0);
+       post_parse(&m, ttf);
+       ttf_table_delete(ttf, table);
+    }
+
+    table = ttf_find_table(ttf, TAG_NAME);
+    if(table) {
+       INIT_READ(m, table->data, table->len, 0);
+       name_parse(&m, ttf);
+       ttf_table_delete(ttf, table);
+    }
+
+    table = ttf_find_table(ttf, TAG_CVT);
+    if(table) {
+       INIT_READ(m, table->data, table->len, 0);
+       cvt_parse(&m, ttf);
+       ttf_table_delete(ttf, table);
+    }
+    
+    table = ttf_find_table(ttf, TAG_GASP);
+    if(table) {
+       INIT_READ(m, table->data, table->len, 0);
+       gasp_parse(&m, ttf);
+       ttf_table_delete(ttf, table);
+    }
+    
+    table = ttf_find_table(ttf, TAG_PREP);
+    if(table) {
+       INIT_READ(m, table->data, table->len, 0);
+       prep_parse(&m, ttf);
+       ttf_table_delete(ttf, table);
+    }
+    
+    table = ttf_find_table(ttf, TAG_FPGM);
+    if(table) {
+       INIT_READ(m, table->data, table->len, 0);
+       fpgm_parse(&m, ttf);
+       ttf_table_delete(ttf, table);
+    }
+
     return 1;
 }
 static void ttf_collapse_tables(ttf_t*ttf)
 {
     ttf_table_t*table;
-   
-    table = ttf_addtable(ttf, TAG_MAXP);
-    maxp_write(ttf, table);
-    maxp_delete(ttf);
 
-    table = ttf_addtable(ttf, TAG_OS2);
-    os2_write(ttf, table);
-    os2_delete(ttf);
+    ttf_table_t*head = ttf_find_table(ttf, TAG_HEAD);
+    if(head)
+       return; //already collapsed
 
-    if(!ttf->is_vertical) {
-       table = ttf_addtable(ttf, TAG_HMTX);
-       int num_advances = mtx_write(ttf, table);
-       table = ttf_addtable(ttf, TAG_HHEA);
-       hea_write(ttf, table, num_advances);
-    } else {
-       table = ttf_addtable(ttf, TAG_VMTX);
-       int num_advances = mtx_write(ttf, table);
-       table = ttf_addtable(ttf, TAG_VHEA);
-       hea_write(ttf, table, num_advances);
+    if(ttf->maxp) {
+       table = ttf_addtable(ttf, TAG_MAXP);
+       maxp_write(ttf, table);
+       maxp_delete(ttf);
     }
-       
+
+    if(ttf->os2) {
+       table = ttf_addtable(ttf, TAG_OS2);
+       os2_write(ttf, table);
+       os2_delete(ttf);
+    }
+
+    if(ttf->hea) {
+       if(!ttf->is_vertical) {
+           table = ttf_addtable(ttf, TAG_HMTX);
+           int num_advances = mtx_write(ttf, table);
+           table = ttf_addtable(ttf, TAG_HHEA);
+           hea_write(ttf, table, num_advances);
+           hea_delete(ttf);
+       } else {
+           table = ttf_addtable(ttf, TAG_VMTX);
+           int num_advances = mtx_write(ttf, table);
+           table = ttf_addtable(ttf, TAG_VHEA);
+           hea_write(ttf, table, num_advances);
+           hea_delete(ttf);
+       }
+    }
+
     int loca_size=0;
     if(ttf->num_glyphs) {
-       table = ttf_addtable(ttf, TAG_CMAP);
-       cmap_write(ttf, table);
-       cmap_delete(ttf);
+       if(ttf->unicode) {
+           table = ttf_addtable(ttf, TAG_CMAP);
+           cmap_write(ttf, table);
+           cmap_delete(ttf);
+       }
 
-       table = ttf_addtable(ttf, TAG_GLYF);
-       U32*locations = glyf_write(ttf, table);
-       glyf_delete(ttf);
-       
-       table = ttf_addtable(ttf, TAG_LOCA);
-       loca_size = loca_write(ttf, table, locations);
-       free(locations);
+       if(ttf->glyphs) {
+           table = ttf_addtable(ttf, TAG_GLYF);
+           U32*locations = glyf_write(ttf, table);
+           table = ttf_addtable(ttf, TAG_LOCA);
+           loca_size = loca_write(ttf, table, locations);
+           free(locations);
+           glyf_delete(ttf);
+       }
     }
-    
+
+    if(ttf->full_name || ttf->family_name || ttf->subfamily_name || ttf->font_uid || ttf->postscript_name) {
+       table = ttf_addtable(ttf, TAG_NAME);
+       name_write(ttf, table);
+       name_delete(ttf);
+    }
+    if(ttf->post) {
+       table = ttf_addtable(ttf, TAG_POST);
+       post_write(ttf, table);
+       post_delete(ttf);
+    }
+    if(ttf->cvt) {
+       table = ttf_addtable(ttf, TAG_CVT);
+       cvt_write(ttf, table);
+       cvt_delete(ttf);
+    }
+    if(ttf->gasp) {
+       table = ttf_addtable(ttf, TAG_GASP);
+       gasp_write(ttf, table);
+       gasp_delete(ttf);
+    }
+    if(ttf->fpgm) {
+       table = ttf_addtable(ttf, TAG_FPGM);
+       fpgm_write(ttf, table);
+       fpgm_delete(ttf);
+    }
+    if(ttf->prep) {
+       table = ttf_addtable(ttf, TAG_PREP);
+       prep_write(ttf, table);
+       prep_delete(ttf);
+    }
+
     table = ttf_addtable(ttf, TAG_HEAD);
     head_write(ttf, table, loca_size);
     head_delete(ttf);
@@ -1589,6 +2053,62 @@ ttf_t* ttf_load(void*data, int length)
 
     ttf_t*ttf = rfx_calloc(sizeof(ttf_t));
     ttf->version = readU32(&r);
+    if(ttf->version == SWAP32(length)) {
+       U32 fontDataSize = readU32(&r);
+       U32 version = readU32(&r);
+       U32 flags  = readU32(&r);
+       U8 panose[10];
+       readBlock(&r, panose, 10);
+       readU8(&r); //charset
+       readU8(&r); //italoc
+       readU32(&r); //weight
+       readU16(&r); //fstype
+       U16 magic = readU16(&r); //magicNumber
+       /* we're being paranoid: it's entirely possible for the font
+          size to be exactly 0x10000. Only treat this font as eot if
+          it has the right magic number */
+       if(magic == 0x4c50) {
+           readU32(&r); //unicoderange[0]
+           readU32(&r); //unicoderange[1]
+           readU32(&r); //unicoderange[2]
+           readU32(&r); //unicoderange[3]
+           readU32(&r); //codepagerange[0]
+           readU32(&r); //codepagerange[1]
+           readU32(&r); //checksumadjustment
+           readU32(&r); //reserved[0]
+           readU32(&r); //reserved[1]
+           readU32(&r); //reserved[2]
+           readU32(&r); //reserved[3]
+           readU16(&r); //padding
+
+           int nr=0;
+           for(nr=0;nr<4;nr++) {
+               int t, len;
+               /* All of ttf is big-endian. All of ttf? No. One small eot table
+                  of indomitable little-endian... */
+               len = readU8(&r);
+               len |= readU8(&r)<<8;
+               len /= 2;
+               for(t=0;t<len;t++) {
+                   U8 c = readU16(&r)>>8;
+               }
+               readU16(&r); // zero terminator
+           }
+           readU16(&r); // more padding
+
+           /* adjust the offset to the start of the actual truetype
+              data- the positions in the table header will be relative
+              to the ttf data after the header, not to the file */
+           r.mem += r.pos;
+           r.size -= r.pos;
+           r.pos = 0;
+           ttf->version = readU32(&r);
+       } else {
+           reader_reset(&r);
+           ttf->version = readU32(&r);
+       }
+    }
+
     if(ttf->version == TTCFTAG) {
        /* a ttc collection is a number of truetype fonts
           packaged together */
@@ -1606,17 +2126,17 @@ ttf_t* ttf_load(void*data, int length)
        r.pos = font1_position;
        ttf->version = readU32(&r);
     }
-    
+
     int num_tables = readU16(&r);
-    
+
     readU16(&r); //search range
     readU16(&r); //entry selector
     readU16(&r); //range shift
 
     if(num_tables*16 > length) {
-       msg("<error> Truncated TTC file (table entries: %d)", num_tables);
-       if(ttf->version != OPENTYPE && 
-          ttf->version != TRUETYPE_MACOS && 
+       msg("<error> Truncated TTF file (table entries: %d)", num_tables);
+       if(ttf->version != OPENTYPE &&
+          ttf->version != TRUETYPE_MACOS &&
           ttf->version != VERSION_1_0) {
            // bad table length, weird version. This is probably not a ttf file.
            return 0;
@@ -1633,21 +2153,21 @@ ttf_t* ttf_load(void*data, int length)
        U32 checksum = table_data[t*4+1];
        U32 pos = table_data[t*4+2];
        U32 len = table_data[t*4+3];
-
+       
        if(pos+len > length) {
            msg("<error> TTF Table %02x%02x%02x%02x outside of stream (pos %d)", (tag>>24)&0xff, (tag>>16)&0xff, (tag>>8)&0xff, (tag)&0xff, pos);
        } else {
            U8*mem = malloc(len);
            r.pos = pos;
            readBlock(&r, mem, len);
-           
+
            ttf_table_t*table = ttf_addtable(ttf, tag);
            table->data = mem;
            table->len = table->memsize = len;
 #if 0
            U32 checksum2 = ttf_table_checksum(table);
            if(checksum2!=checksum) {
-               msg("<warning> Checksum mismatch in tag %02x%02x%02x%02x %c%c%c%c (%d bytes) %08x!=%08x", 
+               msg("<warning> Checksum mismatch in tag %02x%02x%02x%02x %c%c%c%c (%d bytes) %08x!=%08x",
                        (tag>>24)&0xff, (tag>>16)&0xff, (tag>>8)&0xff, (tag)&0xff,
                        (tag>>24)&0xff, (tag>>16)&0xff, (tag>>8)&0xff, (tag)&0xff,
                        len, checksum2, checksum);
@@ -1657,26 +2177,33 @@ ttf_t* ttf_load(void*data, int length)
     }
     free(table_data);
 
-    if(!ttf_parse_tables(ttf)) 
+    if(!ttf_parse_tables(ttf))
        return 0;
 
     return ttf;
 }
 void ttf_create_truetype_tables(ttf_t*ttf)
 {
-    if(!ttf->head) 
+    if(!ttf->head)
        ttf->head = head_new(ttf);
-    if(!ttf->maxp) 
+    if(!ttf->maxp)
        ttf->maxp = maxp_new(ttf);
     if(!ttf->hea)
        ttf->hea = hea_new(ttf);
     if(!ttf->os2)
        ttf->os2 = os2_new(ttf);
+    if(!ttf->post)
+       ttf->post = post_new(ttf);
+    if(!ttf->gasp)
+       ttf->gasp = gasp_new(ttf);
+    if(!ttf->prep)
+       ttf->prep = prep_new(ttf);
 }
-ttf_table_t* ttf_write(ttf_t*ttf)
+
+ttf_table_t* ttf_write(ttf_t*ttf, U32*checksum_adjust)
 {
     ttf_collapse_tables(ttf);
-   
+
     ttf_table_t*file = ttf_table_new(0);
     writeU32(file, VERSION_1_0);
 
@@ -1688,7 +2215,7 @@ ttf_table_t* ttf_write(ttf_t*ttf)
        t = t->next;
     }
     writeU16(file, num_tables);
-    
+
     /* write search range */
     int tmp = num_tables;
     int search_range = 0;
@@ -1734,6 +2261,8 @@ ttf_table_t* ttf_write(ttf_t*ttf)
        writeBlock(file, zero, (-t->len)&3); //pad
     }
     U32 checksum = 0xb1b0afba - ttf_table_checksum(file);
+    if(checksum_adjust)
+       *checksum_adjust = checksum;
     U8*checksum2 = file->data + head_pos + 8;
     checksum2[0] = checksum>>24;
     checksum2[1] = checksum>>16;
@@ -1741,9 +2270,107 @@ ttf_table_t* ttf_write(ttf_t*ttf)
     checksum2[3] = checksum>>0;
     return file;
 }
+
+ttf_table_t* ttf_eot_head(ttf_t*ttf)
+{
+    ttf_table_t*file = ttf_table_new(0);
+    writeU32(file, 0); //file size (filled in later)
+    writeU32(file, 0); //fontdatasize (filled in later)
+    writeU32(file, 0x01000200);
+    writeU32(file, 0); //flags
+    writeU8(file, ttf->os2->panose_FamilyType);
+    writeU8(file, ttf->os2->panose_SerifStyle);
+    writeU8(file, ttf->os2->panose_Weight);
+    writeU8(file, ttf->os2->panose_Proportion);
+    writeU8(file, ttf->os2->panose_Contrast);
+    writeU8(file, ttf->os2->panose_StrokeVariation);
+    writeU8(file, ttf->os2->panose_ArmStyle);
+    writeU8(file, ttf->os2->panose_Letterform);
+    writeU8(file, ttf->os2->panose_Midline);
+    writeU8(file, ttf->os2->panose_XHeight);
+    writeU8(file, 1); //charset (default)
+    writeU8(file, ttf->os2->fsSelection&1); //italic
+    writeU32_LE(file, ttf->os2->usWeightClass);
+    writeU16(file, 0); //fstype
+    writeU16(file, 0x4c50); //magic
+    writeU32_LE(file, ttf->os2->ulCharRange[0]);
+    writeU32_LE(file, ttf->os2->ulCharRange[1]);
+    writeU32_LE(file, ttf->os2->ulCharRange[2]);
+    writeU32_LE(file, ttf->os2->ulCharRange[3]);
+    writeU32_LE(file, ttf->os2->ulCodePageRange1);
+    writeU32_LE(file, ttf->os2->ulCodePageRange2);
+    writeU32(file, 0); //checksum adjust (filled in later)
+    writeU32(file, 0); //reserved[0]
+    writeU32(file, 0); //reserved[1]
+    writeU32(file, 0); //reserved[2]
+    writeU32(file, 0); //reserved[3]
+    writeU16(file, 0); //padding(1)
+
+    int i,t,len;
+
+    char* strings[] = {ttf->family_name, ttf->subfamily_name, ttf->version_string, ttf->full_name};
+    int nr = sizeof(strings)/sizeof(strings[0]);
+
+    for(i=0;i<nr;i++) {
+       char *string = strings[i];
+
+       //family name
+       len = strlen(string);
+       writeU16_LE(file, len*2);
+       for(t=0;t<len;t++) {
+           writeU8(file, 0);
+           writeU8(file, string[t]);
+       }
+       writeU16(file, 0); //zero byte pad
+    }
+
+    writeU16(file, 0); //zero byte pad
+
+    writeU16(file, 0); //padding(2)
+    return file;
+}
+
+void ttf_save_eot(ttf_t*ttf, const char*filename)
+{
+    ttf_table_t* eot = ttf_eot_head(ttf);
+    U32 checksum_adjust = 0;
+    ttf_table_t* t = ttf_write(ttf, &checksum_adjust);
+
+    U8*len_data = eot->data;
+    U32 full_len = eot->len + t->len;
+    len_data[0] = full_len>>0;
+    len_data[1] = full_len>>8;
+    len_data[2] = full_len>>16;
+    len_data[3] = full_len>>24;
+
+    U8*len_data2 = eot->data+4;
+    len_data2[0] = t->len>>0;
+    len_data2[1] = t->len>>8;
+    len_data2[2] = t->len>>16;
+    len_data2[3] = t->len>>24;
+
+    U8*checksum_data = eot->data + 60;
+    checksum_data[0] = checksum_adjust>>0;
+    checksum_data[1] = checksum_adjust>>8;
+    checksum_data[2] = checksum_adjust>>16;
+    checksum_data[3] = checksum_adjust>>24;
+
+    FILE*fi = fopen(filename, "wb");
+    if(!fi) {
+       perror(filename);
+       return;
+    }
+
+    fwrite(eot->data, eot->len, 1, fi);
+    fwrite(t->data, t->len, 1, fi);
+    fclose(fi);
+    ttf_table_delete(0, t);
+    ttf_table_delete(0, eot);
+}
+
 void ttf_save(ttf_t*ttf, const char*filename)
 {
-    ttf_table_t* t = ttf_write(ttf);
+    ttf_table_t* t = ttf_write(ttf, 0);
     FILE*fi = fopen(filename, "wb");
     if(!fi) {
        perror(filename);
@@ -1753,14 +2380,15 @@ void ttf_save(ttf_t*ttf, const char*filename)
     fclose(fi);
     ttf_table_delete(0, t);
 }
+
 void ttf_dump(ttf_t*ttf)
 {
     msg("<notice> Truetype file version %08x%s", ttf->version, ttf->version == OPENTYPE?" (opentype)":"");
     ttf_table_t*table = ttf->tables;
     while(table) {
        U32 tag = table->id;
-       msg("<notice> Tag %02x%02x%02x%02x [%c%c%c%c] (length: %d)", 
-               (tag>>24)&0xff, (tag>>16)&0xff, (tag>>8)&0xff, (tag)&0xff, 
+       msg("<notice> Tag %02x%02x%02x%02x [%c%c%c%c] (length: %d)",
+               (tag>>24)&0xff, (tag>>16)&0xff, (tag>>8)&0xff, (tag)&0xff,
                (tag>>24)&0xff, (tag>>16)&0xff, (tag>>8)&0xff, (tag)&0xff, table->len);
        table = table->next;
     }
@@ -1772,7 +2400,7 @@ void ttf_dump(ttf_t*ttf)
     maxp_dump(ttf);
     glyf_dump(ttf);
 }
-void ttf_destroy(ttf_t*ttf)
+void ttf_destroy_tables(ttf_t*ttf)
 {
     ttf_table_t*table = ttf->tables;
     while(table) {
@@ -1781,11 +2409,23 @@ void ttf_destroy(ttf_t*ttf)
        free(table);
        table = next;
     }
+    ttf->tables = 0;
+}
+void ttf_reduce(ttf_t*ttf)
+{
+    ttf_destroy_tables(ttf);
+}
+void ttf_destroy(ttf_t*ttf)
+{
+    ttf_destroy_tables(ttf);
     maxp_delete(ttf);
     os2_delete(ttf);
     head_delete(ttf);
     hea_delete(ttf);
     glyf_delete(ttf);
+    post_delete(ttf);
+    cvt_delete(ttf);
+    name_delete(ttf);
     free(ttf);
 }
 
@@ -1794,17 +2434,27 @@ int main(int argn, const char*argv[])
 {
     setConsoleLogging(7);
     const char*filename = "comic.ttf";
-    if(argn>1) 
+    if(argn>1)
        filename = argv[1];
     //msg("<notice> Loading %s", filename);
     memfile_t*m = memfile_open(filename);
     ttf_t*ttf = ttf_load(m->data, m->len);
+
+    if(!ttf) {
+       msg("<error> Couldn't load %s", filename);
+       return 1;
+    }
+    ttf_reduce(ttf);
+
+    ttf_create_truetype_tables(ttf);
+
     if(!ttf) return 1;
     memfile_close(m);
     //ttf_dump(ttf);
-    //printf("os2 version: %04x (%d), maxp size: %d\n", 
+    //printf("os2 version: %04x (%d), maxp size: %d\n",
 //         ttf->os2->version, ttf->os2->size, ttf->maxp->size);
-    ttf_save(ttf, "output.ttf");
+    ttf_save_eot(ttf, "testfont.eot");
+    ttf_save(ttf, "testfont.ttf");
     ttf_destroy(ttf);
     return 0;