small bug in ttf reading
[swftools.git] / lib / ttf.c
index 5aa6e10..c1fd06d 100644 (file)
--- a/lib/ttf.c
+++ b/lib/ttf.c
@@ -22,6 +22,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <memory.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <memory.h>
+#include <assert.h>
 #include "log.h"
 #include "os.h"
 #include "q.h"
 #include "log.h"
 #include "os.h"
 #include "q.h"
 #define TAG_NAME 0x6e616d65
 #define TAG_POST 0x706f7374
 #define TAG_CFF  0x43464620 //required for opentype
 #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)
 {
 
 static U32 checksum_block(U8*_data, int len)
 {
@@ -117,6 +116,10 @@ static void readBlock(memreader_t*r, void*dest, int len)
        r->pos += 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)
 #define INIT_READ(r,data,length,pos) memreader_t r = {(data),(pos),(length)};
 
 static void expand(ttf_table_t*w, int newsize)
@@ -139,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;
 }
     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)
 {
 #define writeS16 writeU16
 static inline void writeU32(ttf_table_t*w, unsigned long v)
 {
@@ -149,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;
 }
     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)
 static inline void writeBlock(ttf_table_t*w, void*data, int len)
 {
     if(w->memsize<w->len+len)
@@ -179,7 +198,7 @@ ttf_table_t*ttf_addtable(ttf_t*ttf, U32 id)
        before->len = 0;
        return before;
     }
        before->len = 0;
        return before;
     }
-    
+
     if(!after) {
        t->next = ttf->tables;
        ttf->tables = t;
     if(!after) {
        t->next = ttf->tables;
        ttf->tables = t;
@@ -196,7 +215,7 @@ ttf_table_t*ttf_find_table(ttf_t*ttf, U32 id)
 {
     ttf_table_t*table = ttf->tables;
     while(table) {
 {
     ttf_table_t*table = ttf->tables;
     while(table) {
-       if(table->id == id) 
+       if(table->id == id)
            return table;
        table = table->next;
     }
            return table;
        table = table->next;
     }
@@ -207,9 +226,9 @@ void ttf_table_delete(ttf_t*ttf, ttf_table_t*table)
     if(ttf && ttf->tables == table) {
        ttf->tables = table->next;
     }
     if(ttf && ttf->tables == table) {
        ttf->tables = table->next;
     }
-    if(table->prev) 
+    if(table->prev)
        table->prev->next = table->next;
        table->prev->next = table->next;
-    if(table->next) 
+    if(table->next)
        table->next->prev = table->prev;
     free(table->data);
     free(table);
        table->next->prev = table->prev;
     free(table->data);
     free(table);
@@ -284,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);
 {
     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);
        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);
        msg("<warning> Font HEAD has unknown magic number %08x", magic);
     ttf->head->flags = readU16(r);
     ttf->head->units_per_em = readU16(r);
@@ -368,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;
     /* 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);
     if(ttf->head) {
        int advance = (ttf->head->xmax - ttf->head->xmin)/2;
        int height = (ttf->head->xmax - ttf->head->xmin);
@@ -384,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->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;
     }
        os2->sxHeight = ymid;
        os2->sCapHeight = height*2/3;
     }
@@ -477,7 +496,7 @@ static table_os2_t*os2_parse(memreader_t*r)
     os2->usDefaultChar = readU16(r);
     os2->usBreakChar = readU16(r);
     os2->usMaxContext = readU16(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);
     }
     if(r->pos < r->size) {
        msg("<warning> Leftover bytes (%d) in OS2 tag", r->size - r->pos);
     }
@@ -619,13 +638,14 @@ static table_maxp_t*maxp_new(ttf_t*ttf)
        maxp->maxComponentPoints = 0;
        maxp->maxComponentContours = 0;
     }
        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);
     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;
        apparently only contain the number of glyphs. this is
        rather rare, though. */
     if(version<0x10000 && r->size==6) return 0;
@@ -707,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;
        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 < hea->minLeftSideBearing)
-               hea->minLeftSideBearing = ttf->glyphs[t].xmin;
+           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;
        }
            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;
 }
     }
     return hea;
 }
@@ -809,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++) {
        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);
     }
 }
        ttf->glyphs[t].bearing = readS16(r);
     }
 }
@@ -830,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);
     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++) {
     }
     for(;t<ttf->num_glyphs;t++) {
-       writeU16(w, ttf->glyphs[t].bearing);
+       writeS16(w, ttf->glyphs[t].bearing);
     }
     return num_advances;
 }
     }
     return num_advances;
 }
@@ -929,15 +949,15 @@ static int parse_simple_glyph(ttf_t*ttf, memreader_t*r, int num_contours, int gl
        glyph->code_size = code_len;
     }
 
        glyph->code_size = code_len;
     }
 
-    if(!endpoints) 
+    if(!endpoints)
        return 1;
 
        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);
            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);
 
     glyph->num_points = endpoints[num_contours-1] + 1;
     glyph->points = rfx_calloc(sizeof(ttfpoint_t)*glyph->num_points);
 
@@ -953,7 +973,7 @@ static int parse_simple_glyph(ttf_t*ttf, memreader_t*r, int num_contours, int gl
            return 0;
        }
        int count = 1;
            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);
            count += readU8(r);
        if(count+num>glyph->num_points) {
            msg("<warning> Bad count (%d) in glyph (%d) (at pos %d)", count, glyphnr, num);
@@ -982,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);
            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;
            glyph->points[num].x = x;
            U8 f = flag&GLYPH_ON_CURVE;
            if(is_start) f|=GLYPH_CONTOUR_START;
@@ -992,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);
     }
            is_start = is_end;
        } while(--count);
     }
-    
+
     /* parse flag array (3rd pass) and y coordinates */
     num=0;
     int y = 0;
     /* parse flag array (3rd pass) and y coordinates */
     num=0;
     int y = 0;
@@ -1020,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) {
        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;
        }
                    t, ttf->num_glyphs, loca[t], r.size, loca[t+1]);
            break;
        }
@@ -1029,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);
        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");
        if(num_contours<0) {
            if(warn_about_compound_glyphs)
                msg("<error> Compound glyphs not supported yet");
@@ -1049,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);
     }
        if(g->points[s].flags&GLYPH_CONTOUR_END)
            writeU16(w, s);
     }
-    
+
     /* bytecode */
     writeU16(w, g->code_size);
     if(g->code_size)
     /* bytecode */
     writeU16(w, g->code_size);
     if(g->code_size)
@@ -1150,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);
        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) */
        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) */
@@ -1183,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");
            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)
 {
            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++) {
        return;
     int t;
     for(t=0;t<ttf->num_glyphs;t++) {
@@ -1240,7 +1260,7 @@ void cmap_parse(memreader_t*r, ttf_t*ttf)
                         platform==3 && encoding == 1 ||
                         platform==3 && encoding == 10;
 
                         platform==3 && encoding == 1 ||
                         platform==3 && encoding == 10;
 
-       if(!is_unicode) 
+       if(!is_unicode)
            continue;
 
        INIT_READ(t, r->mem, r->size, offset);
            continue;
 
        INIT_READ(t, r->mem, r->size, offset);
@@ -1265,7 +1285,7 @@ void cmap_parse(memreader_t*r, ttf_t*ttf)
                ttf->unicode[s] = readU8(&t);
            }
        } else if(format == 4) {
                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;
            if(segment_count&1) {
                msg("<error> Bad segmentx2 count %d", segment_count);
                continue;
@@ -1287,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) {
                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.
                       "glyph undefined" (0).
                       We don't want to blow our unicode table up to 65536 just
                       because of this, so ignore this entry.
@@ -1324,7 +1344,7 @@ static int segment_size(unicode_t*unicode, int pos, int size)
        if(!unicode[s])
            count++;
        if(count>4) {
        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;
               zero entries (16 bit each) in the glyph index array,
               it pays off to start a new segment */
            break;
@@ -1373,7 +1393,7 @@ 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;
     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
     int t;
     int end_pos = w->len;
     for(t=0;t<num_segments;t++) {writeU16(w, 0);} //end array
@@ -1384,10 +1404,30 @@ 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
     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;
     while(pos < ttf->unicode_size) {
     pos=0;
     num_segments = 0;
     while(pos < ttf->unicode_size) {
@@ -1450,12 +1490,19 @@ void cmap_delete(ttf_t*ttf)
     }
     ttf->unicode_size=0;
 }
     }
     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);
 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);
     int t;
     for(t=0;t<count;t++) {
        U16 platform = readU16(r);
@@ -1464,37 +1511,122 @@ void name_parse(memreader_t*r, ttf_t*ttf)
        U16 name_id = readU16(r);
        U16 len = readU16(r);
        U16 offset_2 = readU16(r);
        U16 name_id = readU16(r);
        U16 len = readU16(r);
        U16 offset_2 = readU16(r);
-       if(name_id==4) {
-           if(ttf->name)
-               free(ttf->name);
-           ttf->name = strdup_n(&r->mem[offset+offset_2], len);
+
+       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)
 {
        }
     }
 }
 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
     writeU16(table, 0); //format
-    writeU16(table, 1); //count
-    int offset = 18;
-    writeU16(table, offset); //offset
-
-    writeU16(table, 1); //platform id
-    writeU16(table, 0); //encoding id
-    writeU16(table, 0); //language
-    writeU16(table, 4); //4: full name
-    int len = strlen(ttf->name);
-    writeU16(table, len);
-    writeU16(table, table->len+2 - offset);
+    int count = 0;
     int t;
     int t;
-    for(t=0;t<len;t++) {
-       writeU8(table, ttf->name[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)
 {
     }
 }
 void name_delete(ttf_t*ttf)
 {
-    if(ttf->name) {
-       free(ttf->name);
-       ttf->name=0;
+    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;
     }
 }
 
     }
 }
 
@@ -1506,15 +1638,15 @@ static table_post_t*post_new(ttf_t*ttf)
 void post_parse(memreader_t*r, ttf_t*ttf)
 {
     table_post_t*post = ttf->post = rfx_calloc(sizeof(table_post_t));
 void post_parse(memreader_t*r, ttf_t*ttf)
 {
     table_post_t*post = ttf->post = rfx_calloc(sizeof(table_post_t));
-    U16 format = readU16(r);
-    post->italic_angle = readU16(r);
+    U32 format = readU32(r);
+    post->italic_angle = readU32(r);
     post->underline_position = readU16(r);
     post->underline_thickness = readU16(r);
     post->underline_position = readU16(r);
     post->underline_thickness = readU16(r);
-    U16 is_monospaced = readU16(r);
-    readU16(r); // min mem 42
-    readU16(r);
-    readU16(r); // min mem 1
-    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)
 {
 }
 void post_write(ttf_t*ttf, ttf_table_t*table)
 {
@@ -1537,6 +1669,152 @@ void post_delete(ttf_t*ttf)
     }
 }
 
     }
 }
 
+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)
 {
     ttf_table_t*table;
 static int ttf_parse_tables(ttf_t*ttf)
 {
     ttf_table_t*table;
@@ -1549,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);
     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");
     table = ttf_find_table(ttf, TAG_MAXP);
     if(!table) {
        msg("<error> Font has no maxp table");
@@ -1558,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);
     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;
     if(!ttf->num_glyphs) {
        msg("<error> Invalid number of characters");
        return 0;
@@ -1572,12 +1850,13 @@ static int ttf_parse_tables(ttf_t*ttf)
         ttf_table_delete(ttf, table);
     }
 
         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_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);
        table = ttf_find_table(ttf, TAG_HMTX);
        if(table) {
            INIT_READ(m, table->data, table->len, 0);
@@ -1615,14 +1894,21 @@ static int ttf_parse_tables(ttf_t*ttf)
        }
        free(loca);
     }
        }
        free(loca);
     }
-    
+
     table = ttf_find_table(ttf, TAG_CMAP);
     if(table) {
        INIT_READ(m, table->data, table->len, 0);
        cmap_parse(&m, ttf);
        ttf_table_delete(ttf, table);
     }
     table = ttf_find_table(ttf, TAG_CMAP);
     if(table) {
        INIT_READ(m, table->data, table->len, 0);
        cmap_parse(&m, 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);
     table = ttf_find_table(ttf, TAG_NAME);
     if(table) {
        INIT_READ(m, table->data, table->len, 0);
@@ -1630,10 +1916,31 @@ static int ttf_parse_tables(ttf_t*ttf)
        ttf_table_delete(ttf, table);
     }
 
        ttf_table_delete(ttf, table);
     }
 
-    table = ttf_find_table(ttf, TAG_POST);
+    table = ttf_find_table(ttf, TAG_CVT);
     if(table) {
        INIT_READ(m, table->data, table->len, 0);
     if(table) {
        INIT_READ(m, table->data, table->len, 0);
-       post_parse(&m, ttf);
+       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);
     }
 
        ttf_table_delete(ttf, table);
     }
 
@@ -1642,43 +1949,58 @@ static int ttf_parse_tables(ttf_t*ttf)
 static void ttf_collapse_tables(ttf_t*ttf)
 {
     ttf_table_t*table;
 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) {
     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->name) {
+    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);
        table = ttf_addtable(ttf, TAG_NAME);
        name_write(ttf, table);
        name_delete(ttf);
@@ -1688,7 +2010,27 @@ static void ttf_collapse_tables(ttf_t*ttf)
        post_write(ttf, table);
        post_delete(ttf);
     }
        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);
     table = ttf_addtable(ttf, TAG_HEAD);
     head_write(ttf, table, loca_size);
     head_delete(ttf);
@@ -1711,6 +2053,62 @@ ttf_t* ttf_load(void*data, int length)
 
     ttf_t*ttf = rfx_calloc(sizeof(ttf_t));
     ttf->version = readU32(&r);
 
     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 */
     if(ttf->version == TTCFTAG) {
        /* a ttc collection is a number of truetype fonts
           packaged together */
@@ -1728,17 +2126,17 @@ ttf_t* ttf_load(void*data, int length)
        r.pos = font1_position;
        ttf->version = readU32(&r);
     }
        r.pos = font1_position;
        ttf->version = readU32(&r);
     }
-    
+
     int num_tables = readU16(&r);
     int num_tables = readU16(&r);
-    
+
     readU16(&r); //search range
     readU16(&r); //entry selector
     readU16(&r); //range shift
 
     if(num_tables*16 > length) {
     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;
           ttf->version != VERSION_1_0) {
            // bad table length, weird version. This is probably not a ttf file.
            return 0;
@@ -1755,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];
        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);
        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) {
            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);
                        (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);
@@ -1779,16 +2177,16 @@ ttf_t* ttf_load(void*data, int length)
     }
     free(table_data);
 
     }
     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)
 {
        return 0;
 
     return ttf;
 }
 void ttf_create_truetype_tables(ttf_t*ttf)
 {
-    if(!ttf->head) 
+    if(!ttf->head)
        ttf->head = head_new(ttf);
        ttf->head = head_new(ttf);
-    if(!ttf->maxp) 
+    if(!ttf->maxp)
        ttf->maxp = maxp_new(ttf);
     if(!ttf->hea)
        ttf->hea = hea_new(ttf);
        ttf->maxp = maxp_new(ttf);
     if(!ttf->hea)
        ttf->hea = hea_new(ttf);
@@ -1796,11 +2194,16 @@ void ttf_create_truetype_tables(ttf_t*ttf)
        ttf->os2 = os2_new(ttf);
     if(!ttf->post)
        ttf->post = post_new(ttf);
        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_collapse_tables(ttf);
-   
+
     ttf_table_t*file = ttf_table_new(0);
     writeU32(file, VERSION_1_0);
 
     ttf_table_t*file = ttf_table_new(0);
     writeU32(file, VERSION_1_0);
 
@@ -1812,7 +2215,7 @@ ttf_table_t* ttf_write(ttf_t*ttf)
        t = t->next;
     }
     writeU16(file, num_tables);
        t = t->next;
     }
     writeU16(file, num_tables);
-    
+
     /* write search range */
     int tmp = num_tables;
     int search_range = 0;
     /* write search range */
     int tmp = num_tables;
     int search_range = 0;
@@ -1858,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);
        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;
     U8*checksum2 = file->data + head_pos + 8;
     checksum2[0] = checksum>>24;
     checksum2[1] = checksum>>16;
@@ -1865,9 +2270,107 @@ ttf_table_t* ttf_write(ttf_t*ttf)
     checksum2[3] = checksum>>0;
     return file;
 }
     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)
 {
 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);
     FILE*fi = fopen(filename, "wb");
     if(!fi) {
        perror(filename);
@@ -1877,14 +2380,15 @@ void ttf_save(ttf_t*ttf, const char*filename)
     fclose(fi);
     ttf_table_delete(0, t);
 }
     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;
 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;
     }
                (tag>>24)&0xff, (tag>>16)&0xff, (tag>>8)&0xff, (tag)&0xff, table->len);
        table = table->next;
     }
@@ -1920,6 +2424,7 @@ void ttf_destroy(ttf_t*ttf)
     hea_delete(ttf);
     glyf_delete(ttf);
     post_delete(ttf);
     hea_delete(ttf);
     glyf_delete(ttf);
     post_delete(ttf);
+    cvt_delete(ttf);
     name_delete(ttf);
     free(ttf);
 }
     name_delete(ttf);
     free(ttf);
 }
@@ -1929,22 +2434,26 @@ int main(int argn, const char*argv[])
 {
     setConsoleLogging(7);
     const char*filename = "comic.ttf";
 {
     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);
        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);
     if(!ttf) {
        msg("<error> Couldn't load %s", filename);
        return 1;
     }
     ttf_reduce(ttf);
-    ttf->name = strdup("testfont");
+
+    ttf_create_truetype_tables(ttf);
+
     if(!ttf) return 1;
     memfile_close(m);
     //ttf_dump(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->os2->version, ttf->os2->size, ttf->maxp->size);
+    ttf_save_eot(ttf, "testfont.eot");
     ttf_save(ttf, "testfont.ttf");
     ttf_destroy(ttf);
     return 0;
     ttf_save(ttf, "testfont.ttf");
     ttf_destroy(ttf);
     return 0;