added truetype parser
authorMatthias Kramm <kramm@quiss.org>
Tue, 16 Feb 2010 04:03:22 +0000 (20:03 -0800)
committerMatthias Kramm <kramm@quiss.org>
Tue, 16 Feb 2010 04:03:22 +0000 (20:03 -0800)
lib/ttf.c [new file with mode: 0644]
lib/ttf.h [new file with mode: 0644]

diff --git a/lib/ttf.c b/lib/ttf.c
new file mode 100644 (file)
index 0000000..fe603c1
--- /dev/null
+++ b/lib/ttf.c
@@ -0,0 +1,1400 @@
+/* ttf.c
+   Parser and writer for truetype font files.
+
+   Part of the swftools package.
+   
+   Copyright (c) 2010 Matthias Kramm <kramm@quiss.org> 
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <memory.h>
+#include "bitio.h"
+#include "log.h"
+#include "os.h"
+#include "mem.h"
+#include "ttf.h"
+
+#define TTCFTAG 0x74746366
+
+#define OPENTYPE 0x4f54544f
+#define TRUETYPE_MACOS 0x74727565
+#define VERSION_1_0 0x00010000
+
+#define TAG_OS2 0x4f532f32
+#define TAG_CMAP 0x636d6170
+#define TAG_GLYF 0x676c7966 //required for non opentype
+#define TAG_HEAD 0x68656164 //required
+#define TAG_HHEA 0x68686561 //required
+#define TAG_HMTX 0x686d7478 //required
+#define TAG_VHEA 0x86686561
+#define TAG_VMTX 0x866d7478
+#define TAG_KERN 0x6b65726e
+#define TAG_LOCA 0x6c6f6361 //required for non opentype
+#define TAG_MAXP 0x6d617870 //required
+#define TAG_NAME 0x6e616d65
+#define TAG_POST 0x706f7374
+#define TAG_CFF  0x43464620 //required for opentype
+
+/* TODO:
+    fpgm - assembly instructions
+    prep - assembly instructions
+    cvt - constant value table
+    gasp - gridfitting procedure
+*/
+
+static U32 checksum_block(U8*_data, int len)
+{
+    U32 sum = 0;
+    U8*data = (U8*)_data;
+
+    int pos;
+    int len_minus_4 = len-4;
+    for(pos=0;pos<=len_minus_4;pos+=4) {
+        sum += data[pos]<<24|data[pos+1]<<16|data[pos+2]<<8|data[pos+3];
+    }
+    int left = len-pos;
+    if(left == 1) sum+= data[pos+0]<<24;
+    if(left == 2) sum+= data[pos+0]<<24|data[pos+1]<<16;
+    if(left == 3) sum+= data[pos+0]<<24|data[pos+1]<<16|data[pos+2]<<8;
+    return sum;
+}
+
+typedef struct _memreader {
+    U8*mem;
+    int pos;
+    int size;
+} memreader_t;
+
+static U8 readU8(memreader_t*r)
+{
+    return r->mem[r->pos++];
+}
+static inline U16 readU16(memreader_t*r)
+{
+    if(r->pos+2>r->size) return 0;
+    U16 val = r->mem[r->pos]<<8|
+             r->mem[r->pos+1];
+    r->pos += 2;
+    return val;
+}
+static S16 readS16(memreader_t*r)
+{
+    return (S16)readU16(r);
+}
+static U32 readU32(memreader_t*r)
+{
+    if(r->pos+4>r->size) return 0;
+    U32 val = r->mem[r->pos]<<24|
+             r->mem[r->pos+1]<<16|
+             r->mem[r->pos+2]<<8|
+             r->mem[r->pos+3];
+    r->pos += 4;
+    return val;
+}
+static void readBlock(memreader_t*r, void*dest, int len)
+{
+    int remaining = r->size-r->pos;
+    if(len > remaining) {
+       memcpy(dest, r->mem+r->pos, remaining);
+       memset(dest+remaining, 0, len - remaining);
+       r->pos += remaining;
+    } else {
+       memcpy(dest, r->mem+r->pos, len);
+       r->pos += len;
+    }
+}
+#define INIT_READ(r,data,length,pos) memreader_t r = {(data),(pos),(length)};
+
+static void expand(ttf_table_t*w, int newsize)
+{
+    int v1 = (newsize+63)&~63;
+    int v2 = w->len + w->len / 2;
+    w->memsize = v1>v2?v1:v2;
+    w->data = rfx_realloc(w->data, w->memsize);
+}
+static void writeU8(ttf_table_t*w, unsigned char b)
+{
+    if(w->memsize<w->len+1)
+       expand(w, w->len+1);
+    w->data[w->len++] = b;
+}
+static void writeU16(ttf_table_t*w, unsigned short v)
+{
+    if(w->memsize<w->len+2)
+       expand(w, w->len+2);
+    w->data[w->len++] = v>>8;
+    w->data[w->len++] = v;
+}
+#define writeS16 writeU16
+static void writeU32(ttf_table_t*w, unsigned long v)
+{
+    if(w->memsize<w->len+4)
+       expand(w, w->len+4);
+    w->data[w->len++] = v>>24;
+    w->data[w->len++] = v>>16;
+    w->data[w->len++] = v>>8;
+    w->data[w->len++] = v;
+}
+static void writeBlock(ttf_table_t*w, void*data, int len)
+{
+    if(w->memsize<w->len+len)
+       expand(w, w->len+len);
+    memcpy(w->data+w->len, data, len);
+    w->len += len;
+}
+
+ttf_table_t*ttf_table_new(U32 id)
+{
+    ttf_table_t*t = rfx_calloc(sizeof(ttf_table_t));
+    t->id = id;
+    return t;
+}
+
+ttf_table_t*ttf_addtable(ttf_t*ttf, U32 id)
+{
+    ttf_table_t*t = ttf_table_new(id);
+
+    ttf_table_t*before,*after=0;
+    for(before=ttf->tables; before&&before->id<id; before=before->next) {
+       after=before;
+    }
+    if(before && before->id == id) {
+       msg("<error> Error: duplicate table %08x", id);
+       free(before->data);
+       before->data = 0;
+       before->len = 0;
+       return before;
+    }
+    
+    if(!after) {
+       t->next = ttf->tables;
+       ttf->tables = t;
+    } else {
+       t->prev = after;
+       t->next = after->next;
+       after->next = t;
+    }
+    if(t->next)
+       t->next->prev = t;
+    return t;
+}
+ttf_table_t*ttf_find_table(ttf_t*ttf, U32 id)
+{
+    ttf_table_t*table = ttf->tables;
+    while(table) {
+       if(table->id == id) 
+           return table;
+       table = table->next;
+    }
+    return 0;
+}
+void ttf_table_delete(ttf_t*ttf, ttf_table_t*table)
+{
+    if(ttf && ttf->tables == table) {
+       ttf->tables = table->next;
+    }
+    if(table->prev) 
+       table->prev->next = table->next;
+    if(table->next) 
+       table->next->prev = table->prev;
+    free(table->data);
+    free(table);
+}
+U32 ttf_table_checksum(ttf_table_t*t)
+{
+    U32 checksum = checksum_block(t->data, t->len);
+    if(t->id==TAG_HEAD && t->len>=12) {
+       /* the checksum for the HEAD table is calculated by masking out
+          the checksumadjust field */
+       U32 adjust = t->data[8]<<24|t->data[9]<<16|t->data[10]<<8|t->data[11];
+       checksum -= adjust;
+    }
+    return checksum;
+}
+static U8 printable(U8 a)
+{
+    if(a<32 || a==127) return '.';
+    else return a;
+}
+static void hexdump(U8*data, int len, const char*prefix)
+{
+    int t;
+    char ascii[32];
+    printf("%s    -=> ",prefix);
+    for(t=0;t<len;t++) {
+       printf("%02x ", data[t]);
+       ascii[t&15] = printable(data[t]);
+       if((t && ((t&15)==15)) || (t==len-1))
+       {
+           int s,p=((t)&15)+1;
+           ascii[p] = 0;
+           for(s=p-1;s<16;s++) {
+               printf("   ");
+           }
+           if(t==len-1)
+               printf(" %s\n", ascii);
+           else
+               printf(" %s\n%s    -=> ",ascii,prefix);
+       }
+    }
+}
+static void ttf_table_dump(ttf_table_t*t, const char*prefix)
+{
+    if(!t) return;
+    hexdump(t->data, t->len, prefix);
+}
+
+static table_os2_t*os2_parse(memreader_t*r)
+{
+    table_os2_t*os2 = rfx_calloc(sizeof(table_os2_t));
+    U16 version = readU16(r);
+    if(version!=0 && version!=1 && version!=2)
+       msg("<warning> Unknown OS2 version: %04x", version);
+    os2->xAvgCharWidth = readS16(r);
+    os2->usWeightClass = readU16(r);
+    os2->usWidthClass = readU16(r);
+    os2->fsType = readU16(r);
+    os2->ySubscriptXSize = readU16(r);
+    os2->ySubscriptYSize = readU16(r);
+    os2->ySubscriptXOffset = readU16(r);
+    os2->ySubscriptYOffset = readU16(r);
+    os2->ySuperscriptXSize = readU16(r);
+    os2->ySuperscriptYSize = readU16(r);
+    os2->ySuperscriptXOffset = readU16(r);
+    os2->ySuperscriptYOffset = readU16(r);
+    os2->yStrikeoutSize = readU16(r);
+    os2->yStrikeoutPosition = readU16(r);
+    os2->sFamilyClass = readU16(r);
+    os2->panose_FamilyType = readU8(r);
+    os2->panose_SerifStyle = readU8(r);
+    os2->panose_Weight = readU8(r);
+    os2->panose_Proportion = readU8(r);
+    os2->panose_Contrast = readU8(r);
+    os2->panose_StrokeVariation = readU8(r);
+    os2->panose_ArmStyle = readU8(r);
+    os2->panose_Letterform = readU8(r);
+    os2->panose_Midline = readU8(r);
+    os2->panose_XHeight = readU8(r);
+    os2->ulCharRange[0] = readU32(r);
+    os2->ulCharRange[1] = readU32(r);
+    os2->ulCharRange[2] = readU32(r);
+    os2->ulCharRange[3] = readU32(r);
+    os2->achVendID[0] = readU8(r);
+    os2->achVendID[1] = readU8(r);
+    os2->achVendID[2] = readU8(r);
+    os2->achVendID[3] = readU8(r);
+    os2->fsSelection = readU16(r);
+    os2->fsFirstCharIndex = readU16(r);
+    os2->fsLastCharIndex = readU16(r);
+    os2->sTypoAscender = readS16(r);
+    os2->sTypoDescender = readS16(r);
+    os2->sTypoLineGap = readS16(r);
+    os2->usWinAscent = readU16(r);
+    os2->usWinDescent = readU16(r);
+    if(version<1) return os2;
+    os2->ulCodePageRange1 = readU32(r);
+    os2->ulCodePageRange2 = readU32(r);
+    if(version<2) return os2;
+    os2->sxHeight = readS16(r);
+    os2->sCapHeight = readS16(r);
+    os2->usDefaultChar = readU16(r);
+    os2->usBreakChar = readU16(r);
+    os2->usMaxContext = readU16(r);
+    return os2;
+}
+
+static os2_write(ttf_t*ttf, ttf_table_t*w)
+{
+    table_os2_t*os2 = ttf->os2;
+    U16 version=1;
+    if(os2->sxHeight|os2->sCapHeight|os2->usDefaultChar|os2->usBreakChar|os2->usMaxContext) {
+       version=2;
+    }
+    writeU16(w, version);
+    writeS16(w, os2->xAvgCharWidth);
+    writeU16(w, os2->usWeightClass);
+    writeU16(w, os2->usWidthClass);
+    writeU16(w, os2->fsType);
+    writeU16(w, os2->ySubscriptXSize);
+    writeU16(w, os2->ySubscriptYSize);
+    writeU16(w, os2->ySubscriptXOffset);
+    writeU16(w, os2->ySubscriptYOffset);
+    writeU16(w, os2->ySuperscriptXSize);
+    writeU16(w, os2->ySuperscriptYSize);
+    writeU16(w, os2->ySuperscriptXOffset);
+    writeU16(w, os2->ySuperscriptYOffset);
+    writeU16(w, os2->yStrikeoutSize);
+    writeU16(w, os2->yStrikeoutPosition);
+    writeU16(w, os2->sFamilyClass);
+    writeU8(w, os2->panose_FamilyType);
+    writeU8(w, os2->panose_SerifStyle);
+    writeU8(w, os2->panose_Weight);
+    writeU8(w, os2->panose_Proportion);
+    writeU8(w, os2->panose_Contrast);
+    writeU8(w, os2->panose_StrokeVariation);
+    writeU8(w, os2->panose_ArmStyle);
+    writeU8(w, os2->panose_Letterform);
+    writeU8(w, os2->panose_Midline);
+    writeU8(w, os2->panose_XHeight);
+    writeU32(w, os2->ulCharRange[0]);
+    writeU32(w, os2->ulCharRange[1]);
+    writeU32(w, os2->ulCharRange[2]);
+    writeU32(w, os2->ulCharRange[3]);
+    writeU8(w, os2->achVendID[0]);
+    writeU8(w, os2->achVendID[1]);
+    writeU8(w, os2->achVendID[2]);
+    writeU8(w, os2->achVendID[3]);
+    writeU16(w, os2->fsSelection);
+    writeU16(w, os2->fsFirstCharIndex);
+    writeU16(w, os2->fsLastCharIndex);
+    writeS16(w, os2->sTypoAscender);
+    writeS16(w, os2->sTypoDescender);
+    writeS16(w, os2->sTypoLineGap);
+    writeU16(w, os2->usWinAscent);
+    writeU16(w, os2->usWinDescent);
+    if(version<1) return;
+    writeU32(w, os2->ulCodePageRange1);
+    writeU32(w, os2->ulCodePageRange2);
+    if(version<2) return;
+    writeS16(w, os2->sxHeight);
+    writeS16(w, os2->sCapHeight);
+    writeU16(w, os2->usDefaultChar);
+    writeU16(w, os2->usBreakChar);
+    writeU16(w, os2->usMaxContext);
+}
+static void os2_delete(ttf_t*ttf)
+{
+    if(ttf->os2)
+       free(ttf->os2);
+    ttf->os2=0;
+}
+
+static os2_dump(ttf_t*ttf)
+{
+    table_os2_t*os2 = ttf->os2;
+    if(!os2) return;
+    printf("os2->xAvgCharWidth: %d\n", os2->xAvgCharWidth);
+    printf("os2->usWeightClass: %d\n", os2->usWeightClass);
+    printf("os2->usWidthClass: %d\n", os2->usWidthClass);
+    printf("os2->fsType: %d\n", os2->fsType);
+    printf("os2->ySubscriptXSize: %d\n", os2->ySubscriptXSize);
+    printf("os2->ySubscriptYSize: %d\n", os2->ySubscriptYSize);
+    printf("os2->ySubscriptXOffset: %d\n", os2->ySubscriptXOffset);
+    printf("os2->ySubscriptYOffset: %d\n", os2->ySubscriptYOffset);
+    printf("os2->ySuperscriptXSize: %d\n", os2->ySuperscriptXSize);
+    printf("os2->ySuperscriptYSize: %d\n", os2->ySuperscriptYSize);
+    printf("os2->ySuperscriptXOffset: %d\n", os2->ySuperscriptXOffset);
+    printf("os2->ySuperscriptYOffset: %d\n", os2->ySuperscriptYOffset);
+    printf("os2->yStrikeoutSize: %d\n", os2->yStrikeoutSize);
+    printf("os2->yStrikeoutPosition: %d\n", os2->yStrikeoutPosition);
+    printf("os2->sFamilyClass: %d\n", os2->sFamilyClass);
+    printf("os2->panose_FamilyType: %d\n", os2->panose_FamilyType);
+    printf("os2->panose_SerifStyle: %d\n", os2->panose_SerifStyle);
+    printf("os2->panose_Weight: %d\n", os2->panose_Weight);
+    printf("os2->panose_Proportion: %d\n", os2->panose_Proportion);
+    printf("os2->panose_Contrast: %d\n", os2->panose_Contrast);
+    printf("os2->panose_StrokeVariation: %d\n", os2->panose_StrokeVariation);
+    printf("os2->panose_ArmStyle: %d\n", os2->panose_ArmStyle);
+    printf("os2->panose_Letterform: %d\n", os2->panose_Letterform);
+    printf("os2->panose_Midline: %d\n", os2->panose_Midline);
+    printf("os2->panose_XHeight: %d\n", os2->panose_XHeight);
+    printf("os2->ulCharRange[0]: %d\n", os2->ulCharRange[0]);
+    printf("os2->ulCharRange[1]: %d\n", os2->ulCharRange[1]);
+    printf("os2->ulCharRange[2]: %d\n", os2->ulCharRange[2]);
+    printf("os2->ulCharRange[3]: %d\n", os2->ulCharRange[3]);
+    printf("os2->achVendID[0]: %d\n", os2->achVendID[0]);
+    printf("os2->achVendID[1]: %d\n", os2->achVendID[1]);
+    printf("os2->achVendID[2]: %d\n", os2->achVendID[2]);
+    printf("os2->achVendID[3]: %d\n", os2->achVendID[3]);
+    printf("os2->fsSelection: %d\n", os2->fsSelection);
+    printf("os2->fsFirstCharIndex: %d\n", os2->fsFirstCharIndex);
+    printf("os2->fsLastCharIndex: %d\n", os2->fsLastCharIndex);
+    printf("os2->sTypoAscender: %d\n", os2->sTypoAscender);
+    printf("os2->sTypoDescender: %d\n", os2->sTypoDescender);
+    printf("os2->sTypoLineGap: %d\n", os2->sTypoLineGap);
+    printf("os2->usWinAscent: %d\n", os2->usWinAscent);
+    printf("os2->usWinDescent: %d\n", os2->usWinDescent);
+    printf("os2->ulCodePageRange1: %d\n", os2->ulCodePageRange1);
+    printf("os2->ulCodePageRange2: %d\n", os2->ulCodePageRange2);
+    printf("os2->sxHeight: %d\n", os2->sxHeight);
+    printf("os2->sCapHeight: %d\n", os2->sCapHeight);
+    printf("os2->usDefaultChar: %d\n", os2->usDefaultChar);
+    printf("os2->usBreakChar: %d\n", os2->usBreakChar);
+    printf("os2->usMaxContext: %d\n", os2->usMaxContext);
+}
+
+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) 
+       msg("<warning> Font HEAD has unknown version %08x", version);
+    U32 revision = readU32(r);
+    if(revision!=VERSION_1_0) 
+       msg("<warning> Font HEAD has unknown revision %08x", revision);
+    U32 checksum2 = readU32(r);
+    U32 magic = readU32(r);
+    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);
+    readU32(r);readU32(r); //created
+    readU32(r);readU32(r); //modified
+    ttf->head->xmin = readU16(r);
+    ttf->head->ymin = readU16(r);
+    ttf->head->xmax = readU16(r);
+    ttf->head->ymax = readU16(r);
+    ttf->head->macStyle = readU16(r);
+    ttf->head->lowest_readable_size = readU16(r); //in pixels
+    ttf->head->dir_hint = readS16(r);
+    int loc_index = readS16(r); //used in 'loca' table
+    if(loc_index>1)
+       msg("<warning> loca index format %d unknown", loc_index);
+    U16 glyph_data_format = readS16(r);
+    if(glyph_data_format!=0)
+       msg("<warning> Font glyph data format unknown: %04x", glyph_data_format);
+    return loc_index;
+}
+
+static void head_write(ttf_t*ttf, ttf_table_t*w, int loca_size)
+{
+    writeU32(w, 0x10000);
+    writeU32(w, 0x10000);
+    writeU32(w, 0); //checksum
+    writeU32(w, 0x5f0f3cf5); //magic
+    writeU16(w, ttf->head->flags);
+    writeU16(w, ttf->head->units_per_em);
+    writeU32(w, 0);writeU32(w, 0); //created
+    writeU32(w, 0);writeU32(w, 0); //modified
+    writeU16(w, ttf->head->xmin);
+    writeU16(w, ttf->head->ymin);
+    writeU16(w, ttf->head->xmax);
+    writeU16(w, ttf->head->ymax);
+    writeU16(w, ttf->head->macStyle);
+    writeU16(w, ttf->head->lowest_readable_size);
+    writeS16(w, ttf->head->dir_hint);
+    writeS16(w, loca_size); //loca index size (32 bit)
+    writeS16(w, 0); //glyph data format
+}
+static void head_dump(ttf_t*ttf)
+{
+    printf("head->flags: %d\n", ttf->head->flags);
+    printf("head->units_per_em: %d\n", ttf->head->units_per_em);
+    printf("head->xmin: %d\n", ttf->head->xmin);
+    printf("head->ymin: %d\n", ttf->head->ymin);
+    printf("head->xmax: %d\n", ttf->head->xmax);
+    printf("head->ymax: %d\n", ttf->head->ymax);
+    printf("head->macStyle: %d\n", ttf->head->macStyle);
+    printf("head->lowest_readable_size: %d\n", ttf->head->lowest_readable_size);
+    printf("head->dir_hint: %d\n", ttf->head->dir_hint);
+}
+static void head_delete(ttf_t*ttf)
+{
+    if(ttf->head) {
+       free(ttf->head);
+       ttf->head=0;
+    }
+}
+
+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) 
+       apparently only contain the number of glyphs. this is
+       rather rare, though. */
+    if(version<0x10000) return 0;
+
+    table_maxp_t*maxp = rfx_calloc(sizeof(table_maxp_t));
+    maxp->maxPoints = readU16(r);
+    maxp->maxContours = readU16(r);
+    maxp->maxComponentPoints = readU16(r);
+    maxp->maxComponentContours = readU16(r);
+    maxp->maxZones = readU16(r);
+    maxp->maxTwilightPoints = readU16(r);
+    maxp->maxStorage = readU16(r);
+    maxp->maxFunctionDefs = readU16(r);
+    maxp->maxInstructionDefs = readU16(r);
+    maxp->maxStackElements = readU16(r);
+    maxp->maxSizeOfInstructions = readU16(r);
+    maxp->maxComponentElements = readU16(r);
+    maxp->maxComponentDepth = readU16(r);
+    return maxp;
+}
+
+static void maxp_write(ttf_t*ttf, ttf_table_t*w)
+{
+    table_maxp_t*maxp = ttf->maxp;
+    writeU32(w, 0x10000); //version
+    writeU16(w, ttf->num_glyphs);
+    writeU16(w, maxp->maxPoints);
+    writeU16(w, maxp->maxContours);
+    writeU16(w, maxp->maxComponentPoints);
+    writeU16(w, maxp->maxComponentContours);
+    writeU16(w, maxp->maxZones);
+    writeU16(w, maxp->maxTwilightPoints);
+    writeU16(w, maxp->maxStorage);
+    writeU16(w, maxp->maxFunctionDefs);
+    writeU16(w, maxp->maxInstructionDefs);
+    writeU16(w, maxp->maxStackElements);
+    writeU16(w, maxp->maxSizeOfInstructions);
+    writeU16(w, maxp->maxComponentElements);
+    writeU16(w, maxp->maxComponentDepth);
+}
+
+static void maxp_dump(ttf_t*ttf)
+{
+    table_maxp_t*maxp = ttf->maxp;
+    if(!maxp) return;
+    printf("maxp->maxPoints: %d\n", maxp->maxPoints);
+    printf("maxp->maxContours: %d\n", maxp->maxContours);
+    printf("maxp->maxComponentPoints: %d\n", maxp->maxComponentPoints);
+    printf("maxp->maxComponentContours: %d\n", maxp->maxComponentContours);
+    printf("maxp->maxZones: %d\n", maxp->maxZones);
+    printf("maxp->maxTwilightPoints: %d\n", maxp->maxTwilightPoints);
+    printf("maxp->maxStorage: %d\n", maxp->maxStorage);
+    printf("maxp->maxFunctionDefs: %d\n", maxp->maxFunctionDefs);
+    printf("maxp->maxInstructionDefs: %d\n", maxp->maxInstructionDefs);
+    printf("maxp->maxStackElements: %d\n", maxp->maxStackElements);
+    printf("maxp->maxSizeOfInstructions: %d\n", maxp->maxSizeOfInstructions);
+    printf("maxp->maxComponentElements: %d\n", maxp->maxComponentElements);
+    printf("maxp->maxComponentDepth: %d\n", maxp->maxComponentDepth);
+}
+
+static void maxp_delete(ttf_t*ttf)
+{
+    if(ttf->maxp)
+       free(ttf->maxp);
+    ttf->maxp=0;
+}
+
+
+static int hea_parse(memreader_t*r, ttf_t*ttf)
+{
+    table_hea_t*hea = ttf->hea = rfx_calloc(sizeof(table_hea_t));
+    U32 version = readU32(r);
+    hea->ascent = readS16(r);
+    hea->descent = readS16(r);
+    hea->lineGap = readS16(r);
+    hea->advanceWidthMax = readU16(r);
+    hea->minLeftSideBearing = readS16(r);
+    hea->minRightSideBearing = readS16(r);
+    hea->xMaxExtent = readS16(r);
+    hea->caretSlopeRise = readS16(r);
+    hea->caretSlopeRun = readS16(r);
+    hea->caretOffset = readS16(r);
+    readS16(r); //reserved[0]
+    readS16(r); //reserved[1]
+    readS16(r); //reserved[2]
+    readS16(r); //reserved[3]
+    S16 metricDataFormat = readS16(r); //should be 0
+    if(metricDataFormat!=0) {
+       msg("<warning> Unknown metric format %d", metricDataFormat);
+    }
+    int num_advances = readU16(r);
+    if(num_advances > ttf->num_glyphs) {
+       msg("<warning> bad number of horizontal metrics: %d", num_advances);
+       num_advances = ttf->num_glyphs;
+    }
+    return num_advances;
+}
+
+static table_hea_t*hea_write(ttf_t*ttf, ttf_table_t*w, int num_advances)
+{
+    table_hea_t*hea = ttf->hea;
+    writeU32(w, 0x00010000);
+    writeS16(w, hea->ascent);
+    writeS16(w, hea->descent);
+    writeS16(w, hea->lineGap);
+    writeU16(w, hea->advanceWidthMax);
+    writeS16(w, hea->minLeftSideBearing);
+    writeS16(w, hea->minRightSideBearing);
+    writeS16(w, hea->xMaxExtent);
+    writeS16(w, hea->caretSlopeRise);
+    writeS16(w, hea->caretSlopeRun);
+    writeS16(w, hea->caretOffset);
+    writeS16(w, 0); //reserved
+    writeS16(w, 0); //reserved
+    writeS16(w, 0); //reserved
+    writeS16(w, 0); //reserved
+    writeS16(w, 0); //metricDataFormat
+    writeU16(w, num_advances);
+    return hea;
+}
+static void hea_dump(ttf_t*ttf)
+{
+    table_hea_t*hea = ttf->hea;
+    const char*dir = ttf->is_vertical?"v":"h";
+    printf("%shea->ascent: %d\n", dir, hea->ascent);
+    printf("%shea->descent: %d\n", dir, hea->descent);
+    printf("%shea->lineGap: %d\n", dir, hea->lineGap);
+    printf("%shea->advanceWidthMax: %d\n", dir, hea->advanceWidthMax);
+    printf("%shea->minLeftSideBearing: %d\n", dir, hea->minLeftSideBearing);
+    printf("%shea->minRightSideBearing: %d\n", dir, hea->minRightSideBearing);
+    printf("%shea->xMaxExtent: %d\n", dir, hea->xMaxExtent);
+    printf("%shea->caretSlopeRise: %d\n", dir, hea->caretSlopeRise);
+    printf("%shea->caretSlopeRun: %d\n", dir, hea->caretSlopeRun);
+    printf("%shea->caretOffset: %d\n", dir, hea->caretOffset);
+}
+static void hea_delete(ttf_t*ttf)
+{
+    if(ttf->hea) {
+       free(ttf->hea);
+       ttf->hea=0;
+    }
+}
+
+static void hmtx_parse(memreader_t*r, ttf_t*ttf, int num_advances)
+{
+    U16 old_advance = 0;
+    int t;
+    if(num_advances > r->size/4)
+       num_advances = r->size/4;
+    for(t=0;t<num_advances;t++) {
+       old_advance = ttf->glyphs[t].advance = readU16(r);
+       ttf->glyphs[t].bearing = readS16(r);
+    }
+    int rest = (r->size - num_advances*4)/2;
+    if(ttf->num_glyphs < num_advances+rest) {
+       rest = ttf->num_glyphs-num_advances;
+    }
+    for(t=0;t<rest;t++) {
+       ttf->glyphs[t].advance = old_advance; 
+       ttf->glyphs[t].bearing = readS16(r);
+    }
+}
+static int mtx_write(ttf_t*ttf, ttf_table_t*w)
+{
+    int num_advances = ttf->num_glyphs;
+    if(ttf->num_glyphs>=2) {
+       int t;
+       for(t=ttf->num_glyphs-1;t>0;t--) {
+           if(ttf->glyphs[t-1].advance !=
+              ttf->glyphs[t].advance) break;
+       }
+       /* we need to store all individual advances as well
+          as one entry for the constant */
+       num_advances = t+1;
+    }
+
+    int t;
+    for(t=0;t<num_advances;t++) {
+       writeU16(w, ttf->glyphs[t].advance);
+       writeU16(w, ttf->glyphs[t].bearing);
+    }
+    for(;t<ttf->num_glyphs;t++) {
+       writeU16(w, ttf->glyphs[t].bearing);
+    }
+    return num_advances;
+}
+static U32*loca_parse(memreader_t*r, ttf_t*ttf, int size)
+{
+    int t;
+    int num = ttf->num_glyphs+1;
+    U32*locations = rfx_calloc(num*sizeof(U32));
+    if(size) {
+       if(num*4 > r->size) {
+           msg("<warning> Short 'loca' table (32 bit)");
+           num=r->size/4;
+       }
+       if(num*4 < r->size) {
+           msg("<warning> Extraneous data (%d bytes) in 'loca' table (32 bit)", r->size-num*4);
+       }
+       for(t=0;t<num;t++) {
+           locations[t] = readU32(r);
+       }
+    } else {
+       if(num*2 > r->size) {
+           msg("<warning> Short 'loca' table (16 bit)");
+           num=r->size/2;
+       }
+       if(num*2 < r->size) {
+           msg("<warning> Extraneous data (%d bytes) in 'loca' table (16 bit)", r->size-num*2);
+       }
+       for(t=0;t<num;t++) {
+           locations[t] = readU16(r)*2;
+       }
+    }
+    return locations;
+}
+static int loca_write(ttf_t*ttf, ttf_table_t*w, U32*locations)
+{
+    int t;
+    char use_32bit = 0;
+    for(t=0;t<=ttf->num_glyphs;t++) {
+       if(locations[t]>=0x20000 || (locations[t]&1)) {
+           use_32bit = 1;
+           break;
+       }
+    }
+
+    if(use_32bit) {
+       for(t=0;t<=ttf->num_glyphs;t++) {
+           writeU32(w, locations[t]);
+       }
+       return 1;
+    } else {
+       for(t=0;t<=ttf->num_glyphs;t++) {
+           writeU16(w, locations[t]/2);
+       }
+       return 0;
+    }
+}
+static int parse_simple_glyph(ttf_t*ttf, memreader_t*r, int num_contours, int glyphnr)
+{
+    ttfglyph_t*glyph = &ttf->glyphs[glyphnr];
+
+    U16*endpoints = 0;
+    if(num_contours>0) {
+       endpoints = malloc(sizeof(U16)*num_contours);
+       int s;
+       int lastpos = -1;
+       for(s=0;s<num_contours;s++) {
+           int pos = endpoints[s] = readU16(r);
+           if(pos<=lastpos) {
+               msg("<warning> Unsorted endpoints array (len:%d) last=%d now=%d", s, pos, lastpos);
+           }
+           lastpos = pos;
+       }
+    }
+    U16 code_len = readU16(r);
+    if(code_len) {
+       glyph->code = malloc(sizeof(U16)*code_len);
+       readBlock(r, glyph->code, code_len);
+       glyph->code_size = code_len;
+    }
+
+    if(!endpoints) 
+       return 1;
+
+    /*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);
+
+    /* parse flag array (1st pass- to determine start of coordinates) */
+    int num=0;
+    while(num<glyph->num_points) {
+       U8 flag = readU8(r);
+       if(flag&0xc0) {
+           msg("<error> Bad flags in glyph outline: %02x (at pos %d)", flag, num);
+           free(glyph->points);
+           glyph->points = 0;
+           glyph->num_points = 0;
+           return 0;
+       }
+       int count = 1;
+       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 = glyph->num_points-num;
+       }
+       num+=count;
+    }
+
+    /* parse flag array (2nd pass) and x coordinates */
+    num=0;
+    int x = 0;
+    char is_start=1;
+    int contour_pos=0;
+    int bytepos = r->pos;
+    while(num<glyph->num_points) {
+       U8 flag = readU8(&fx);
+       int count = flag&8?readU8(&fx)+1:1;
+       count=count>glyph->num_points-num?glyph->num_points-num:(count?count:1);
+       do {
+           char is_end=0;
+           if(contour_pos<num_contours && num==endpoints[contour_pos]) {
+               contour_pos++;
+               is_end=1;
+           }
+           int oldx = x;
+           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;
+           if(is_end) f|=GLYPH_CONTOUR_END;
+           glyph->points[num].flags = f;
+           num++;
+           is_start = is_end;
+       } while(--count);
+    }
+    
+    /* parse flag array (3rd pass) and y coordinates */
+    num=0;
+    int y = 0;
+    while(num<glyph->num_points) {
+       U8 flag = readU8(&fy);
+       int count = flag&8?readU8(&fy)+1:1;
+       count=count>glyph->num_points-num?glyph->num_points-num:(count?count:1);
+       do {
+           if((flag&0x24) == 0x24) y += readU8(r);
+           else if((flag&0x24) == 0x04) y -= readU8(r);
+           else if((flag&0x24) == 0x00) y += readS16(r);
+           glyph->points[num].y = y;
+           num++;
+       } while(--count);
+    }
+    free(endpoints);
+    return 1;
+}
+static void glyf_parse(memreader_t*rr, ttf_t*ttf, U32*loca)
+{
+    int t;
+    char warn_about_compound_glyphs=1;
+    for(t=0;t<ttf->num_glyphs;t++) {
+       INIT_READ(r, rr->mem, rr->size, loca[t]);
+       if(r.pos+10>r.size) {
+           msg("<warning> Unexpected end of glyph array (or bad loca entry %d/%d)", loca[t], r.size);
+           break;
+       }
+       S16 num_contours = readS16(&r);
+       ttf->glyphs[t].xmin = 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");
+           warn_about_compound_glyphs=0;
+       } else {
+           if(!parse_simple_glyph(ttf, &r, num_contours, t))
+               return;
+       }
+    }
+
+}
+
+void write_simple_glyph(ttf_table_t*w, ttfglyph_t*g)
+{
+    /* endpoints array */
+    int s;
+    for(s=0;s<g->num_points;s++) {
+       if(g->points[s].flags&GLYPH_CONTOUR_END)
+           writeU16(w, s);
+    }
+    
+    /* bytecode */
+    writeU16(w, g->code_size);
+    if(g->code_size)
+       writeBlock(w, g->code, g->code_size);
+
+    /* flags */
+    int lastx=0;
+    int lasty=0;
+    int lastflag=-1;
+    int flagcount=0;
+    for(s=0;s<g->num_points;s++) {
+       ttfpoint_t*p = &g->points[s];
+       int dx = p->x - lastx;
+       int dy = p->y - lasty;
+       U8 flags = p->flags&GLYPH_ON_CURVE;
+       if(!dx) {
+           flags|=0x10;
+       } else if(dx<0 && dx>=-255) {
+           flags|=0x02;
+       } else if(dx>0 && dx<=255) {
+           flags|=0x12;
+       }
+       if(!dy) {
+           flags|=0x20;
+       } else if(dy<0 && dy>=-255) {
+           flags|=0x04;
+       } else if(dy>0 && dy<=255) {
+           flags|=0x24;
+       }
+       if(flags == lastflag && flagcount<255) {
+           flagcount++;
+       } else {
+           if(lastflag>=0) {
+               if(flagcount) {
+                   writeU8(w, lastflag|8);
+                   writeU8(w, flagcount);
+               } else {
+                   writeU8(w, lastflag);
+               }
+           }
+           lastflag = flags;
+           flagcount = 0;
+       }
+       lastx = p->x;
+       lasty = p->y;
+    }
+    if(lastflag>=0) {
+       if(flagcount) {
+           writeU8(w, lastflag|8);
+           writeU8(w, flagcount);
+       } else {
+           writeU8(w, lastflag);
+       }
+    }
+    /* coordinates */
+    lastx=0;
+    int bytepos = w->len;
+    for(s=0;s<g->num_points;s++) {
+       ttfpoint_t*p = &g->points[s];
+       int dx = p->x - lastx;
+       if(dx>32767 || dx<-32768) {
+           msg("<error> Coordinate overflow in glyph");
+       }
+       lastx = p->x;
+       if(dx>0 && dx<=255) writeU8(w, dx);
+       else if(dx<0 && dx>=-255) writeU8(w, -dx);
+       else if(dx) writeS16(w, dx);
+    }
+
+    lasty=0;
+    for(s=0;s<g->num_points;s++) {
+       ttfpoint_t*p = &g->points[s];
+       int dy = p->y - lasty;
+       if(dy>32767 || dy<-32768) {
+           msg("<error> Coordinate overflow in glyph");
+       }
+       lasty = p->y;
+       if(dy>0 && dy<=255) writeU8(w, dy);
+       else if(dy<0 && dy>=-255) writeU8(w, -dy);
+       else if(dy) writeS16(w, dy);
+    }
+}
+    
+U32* glyf_write(ttf_t* ttf, ttf_table_t*w)
+{
+    U32*locations = malloc(sizeof(U32)*(ttf->num_glyphs+1));
+    int t;
+    for(t=0;t<ttf->num_glyphs;t++) {
+       locations[t] = w->len;
+       ttfglyph_t*g = &ttf->glyphs[t];
+       int s;
+       int num_contours = 0;
+       for(s=0;s<g->num_points;s++) {
+           if(g->points[s].flags&GLYPH_CONTOUR_END)
+               num_contours++;
+       }
+       writeS16(w, num_contours?num_contours:1);
+       writeS16(w, g->xmin);
+       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) */
+           writeU16(w, 0); //endpoint of 1st contour
+           writeU16(w, g->code_size);
+           if(g->code_size)
+               writeBlock(w, g->code, g->code_size);
+           writeU8(w, 0x31); //flag (xy=(0,0),on curve)
+       } else {
+           write_simple_glyph(w, g);
+       }
+    }
+    locations[t] = w->len;
+    return locations;
+}
+void glyf_dump(ttf_t* ttf)
+{
+    int t;
+    for(t=0;t<ttf->num_glyphs;t++) {
+       ttfglyph_t*g = &ttf->glyphs[t];
+       printf("glyph %d)\n", t);
+       printf("  advance=%d\n", g->advance);
+       printf("  bearing=%d\n", g->bearing);
+       printf("  bbox=(%d/%d/%d/%d)\n", g->xmin, g->ymin, g->xmax, g->ymax);
+       printf("  points=(");
+       int s;
+       for(s=0;s<g->num_points;s++) {
+           if(s) printf(",");
+           printf("%d/%d/0x%02x", g->points[s].x, g->points[s].y, g->points[s].flags);
+       }
+       printf(")\n");
+       if(g->code_size) 
+           hexdump(g->code, g->code_size, "  ");
+    }
+}
+
+void glyf_delete(ttf_t* ttf)
+{
+    if(!ttf->glyphs) 
+       return;
+    int t;
+    for(t=0;t<ttf->num_glyphs;t++) {
+       if(ttf->glyphs[t].code) {
+           free(ttf->glyphs[t].code);
+           ttf->glyphs[t].code = 0;
+       }
+       if(ttf->glyphs[t].points) {
+           free(ttf->glyphs[t].points);
+           ttf->glyphs[t].points = 0;
+       }
+    }
+    free(ttf->glyphs);ttf->glyphs=0;
+}
+
+static int ttf_parse_tables(ttf_t*ttf)
+{
+    ttf_table_t*table;
+
+    table = ttf_find_table(ttf, TAG_HEAD);
+    if(!table) {
+       msg("<error> Font has no head table");
+       return 0;
+    }
+    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");
+       return 0;
+    }
+    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;
+    }
+    ttf->glyphs = rfx_calloc(sizeof(ttfglyph_t)*ttf->num_glyphs);
+
+    table = ttf_find_table(ttf, TAG_OS2);
+    if(table) {
+       INIT_READ(m, table->data, table->len, 0);
+       ttf->os2 = os2_parse(&m);
+        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);
+           hmtx_parse(&m, ttf, num_advances);
+            ttf_table_delete(ttf, table);
+       }
+    } else {
+       table = ttf_find_table(ttf, TAG_VHEA);
+       if(table) {
+           ttf->is_vertical=1;
+           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_VMTX);
+           if(table) {
+               INIT_READ(m, table->data, table->len, 0);
+               hmtx_parse(&m, ttf, num_advances);
+               ttf_table_delete(ttf, table);
+           }
+       } else {
+           msg("<error> Font contains neither HHEA nor VHEA");
+       }
+    }
+    table = ttf_find_table(ttf, TAG_LOCA);
+    if(table) {
+       INIT_READ(m, table->data, table->len, 0);
+       U32*loca = loca_parse(&m, ttf, loc_index);
+       ttf_table_delete(ttf, table);
+       table = ttf_find_table(ttf, TAG_GLYF);
+       if(table) {
+           INIT_READ(m, table->data, table->len, 0);
+           glyf_parse(&m, ttf, loca);
+           ttf_table_delete(ttf, table);
+       }
+       free(loca);
+    }
+    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);
+
+    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);
+    }
+
+    int loca_size=0;
+    if(ttf->num_glyphs) {
+       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);
+    }
+    
+    table = ttf_addtable(ttf, TAG_HEAD);
+    head_write(ttf, table, loca_size);
+    head_delete(ttf);
+
+}
+
+ttf_t* load_ttf(void*data, int length)
+{
+    INIT_READ(r,data,length, 0);
+
+    if(length<12) {
+       msg("<error> Truncated Truetype file (%d bytes)", length);
+       return 0;
+    }
+
+    ttf_t*ttf = rfx_calloc(sizeof(ttf_t));
+    ttf->version = readU32(&r);
+    if(ttf->version == TTCFTAG) {
+       /* a ttc collection is a number of truetype fonts
+          packaged together */
+       if(length<16) {
+           msg("<error> Truncated TTC file (%d bytes)", length);
+           return 0;
+       }
+       U32 ttcf_version = readU32(&r); // 0x00000100: v1.0, 0x00000200: v2.0, includes DSIG table
+       U32 num_fonts = readU32(&r); // number of fonts
+       U32 font1_position = readU32(&r);
+       if(font1_position+12 > length) {\
+           msg("<error> Truncated TTC file (%d bytes, first font at %d)", length, font1_position);
+           return 0;
+       }
+       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 && 
+          ttf->version != VERSION_1_0) {
+           // bad table length, weird version. This is probably not a ttf file.
+           return 0;
+       }
+    }
+
+    U32*table_data = malloc(16*num_tables);
+    int t;
+    for(t=0;t<num_tables*4;t++) {
+       table_data[t] = readU32(&r);
+    }
+    for(t=0;t<num_tables;t++) {
+       U32 tag = table_data[t*4];
+       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;
+           
+           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", 
+                       (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);
+           }
+           
+       }
+    }
+    free(table_data);
+
+    if(!ttf_parse_tables(ttf)) 
+       return 0;
+
+    return ttf;
+}
+
+ttf_table_t* ttf_write(ttf_t*ttf)
+{
+    ttf_collapse_tables(ttf);
+   
+    ttf_table_t*file = ttf_table_new(0);
+    writeU32(file, VERSION_1_0);
+
+    /* write number of tables */
+    int num_tables=0;
+    ttf_table_t*t = ttf->tables;
+    while(t) {
+       num_tables++;
+       t = t->next;
+    }
+    writeU16(file, num_tables);
+    
+    /* write search range */
+    int tmp = num_tables;
+    int search_range = 0;
+    while(tmp) {
+       search_range = tmp;
+       tmp = tmp&(tmp-1);
+    }
+    tmp = search_range;
+    search_range*=16;
+    writeU16(file, search_range);
+
+    /* write entry selector */
+    int entry_selector = 0;
+    while(tmp>1) {
+       tmp>>=1;
+       entry_selector++;
+    }
+    writeU16(file, entry_selector);
+
+    /* write range shift */
+    int range_shift = num_tables*16 - search_range;
+    writeU16(file, range_shift);
+
+    /* write table dictionary */
+    int table_dictionary_pos = file->len;
+    int data_pos = file->len + num_tables*16;
+    for(t=ttf->tables;t;t=t->next) {
+       writeU32(file, t->id);
+       writeU32(file, ttf_table_checksum(t));
+       writeU32(file, data_pos);
+       writeU32(file, t->len);
+       data_pos += t->len;
+       data_pos += (-t->len)&3; //pad
+    }
+
+    /* write tables */
+    int head_pos = 0;
+    U8 zero[4]={0,0,0,0};
+    for(t=ttf->tables;t;t=t->next) {
+       if(t->id == TAG_HEAD)
+           head_pos = file->len;
+       writeBlock(file, t->data, t->len);
+       writeBlock(file, zero, (-t->len)&3); //pad
+    }
+    U32 checksum = 0xb1b0afba - ttf_table_checksum(file);
+    U8*checksum2 = file->data + head_pos + 8;
+    checksum2[0] = checksum>>24;
+    checksum2[1] = checksum>>16;
+    checksum2[2] = checksum>>8;
+    checksum2[3] = checksum>>0;
+    return file;
+}
+
+void ttf_save(ttf_t*ttf, const char*filename)
+{
+    ttf_table_t* t = ttf_write(ttf);
+    FILE*fi = fopen(filename, "wb");
+    if(!fi) {
+       perror(filename);
+       return;
+    }
+    fwrite(t->data, t->len, 1, fi);
+    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, 
+               (tag>>24)&0xff, (tag>>16)&0xff, (tag>>8)&0xff, (tag)&0xff, table->len);
+       table = table->next;
+    }
+    //ttf_table_dump(ttf_find_table(ttf, TAG_MAXP));
+
+    head_dump(ttf);
+    hea_dump(ttf);
+    os2_dump(ttf);
+    maxp_dump(ttf);
+    glyf_dump(ttf);
+}
+void ttf_destroy(ttf_t*ttf)
+{
+    ttf_table_t*table = ttf->tables;
+    while(table) {
+       ttf_table_t*next = table->next;
+       free(table->data);
+       free(table);
+       table = next;
+    }
+    maxp_delete(ttf);
+    os2_delete(ttf);
+    head_delete(ttf);
+    hea_delete(ttf);
+    glyf_delete(ttf);
+    free(ttf);
+}
+
+#ifdef MAIN
+int main(int argn, const char*argv[])
+{
+    setConsoleLogging(7);
+    const char*filename = "comic.ttf";
+    if(argn>1) 
+       filename = argv[1];
+    //msg("<notice> Loading %s", filename);
+    memfile_t*m = memfile_open(filename);
+    ttf_t*ttf = load_ttf(m->data, m->len);
+    if(!ttf) return 1;
+    memfile_close(m);
+    ttf_dump(ttf);
+    //printf("os2 version: %04x (%d), maxp size: %d\n", 
+//         ttf->os2->version, ttf->os2->size, ttf->maxp->size);
+    ttf_save(ttf, "comic2.ttf");
+    ttf_destroy(ttf);
+    return 0;
+
+}
+#endif
diff --git a/lib/ttf.h b/lib/ttf.h
new file mode 100644 (file)
index 0000000..a22c87e
--- /dev/null
+++ b/lib/ttf.h
@@ -0,0 +1,164 @@
+/* ttf.h
+   Parser and writer for truetype font files.
+
+   Part of the swftools package.
+   
+   Copyright (c) 2010 Matthias Kramm <kramm@quiss.org> 
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
+
+#ifndef __ttf_h__
+#define __ttf_h__
+
+#include "types.h"
+
+typedef struct _ttf_table {
+    U32 id;
+    struct _ttf_table*prev;
+    struct _ttf_table*next;
+
+    U8*data;
+    int len;
+    int memsize;
+} ttf_table_t;
+
+typedef struct _table_maxp {
+    U16 maxPoints;
+    U16 maxContours;
+    U16 maxComponentPoints;
+    U16 maxComponentContours;
+    U16 maxZones;
+    U16 maxTwilightPoints;
+    U16 maxStorage;
+    U16 maxFunctionDefs;
+    U16 maxInstructionDefs;
+    U16 maxStackElements;
+    U16 maxSizeOfInstructions;
+    U16 maxComponentElements;
+    U16 maxComponentDepth;
+} table_maxp_t;
+
+typedef struct _table_os2 {
+    S16 xAvgCharWidth;
+    U16 usWeightClass;
+    U16 usWidthClass;
+    U16 fsType;
+    U16 ySubscriptXSize;
+    U16 ySubscriptYSize;
+    U16 ySubscriptXOffset;
+    U16 ySubscriptYOffset;
+    U16 ySuperscriptXSize;
+    U16 ySuperscriptYSize;
+    U16 ySuperscriptXOffset;
+    U16 ySuperscriptYOffset;
+    U16 yStrikeoutSize;
+    U16 yStrikeoutPosition;
+    U16 sFamilyClass;
+    U8 panose_FamilyType;
+    U8 panose_SerifStyle;
+    U8 panose_Weight;
+    U8 panose_Proportion;
+    U8 panose_Contrast;
+    U8 panose_StrokeVariation;
+    U8 panose_ArmStyle;
+    U8 panose_Letterform;
+    U8 panose_Midline;
+    U8 panose_XHeight;
+    U32 ulCharRange[4];
+    U8 achVendID[4];
+
+    U16 fsSelection;
+    U16 fsFirstCharIndex;
+    U16 fsLastCharIndex;
+
+    S16 sTypoAscender;
+    S16 sTypoDescender;
+    S16 sTypoLineGap;
+    U16 usWinAscent;
+    U16 usWinDescent;
+
+    /* for version >= 0x0001 */
+    U32 ulCodePageRange1;
+    U32 ulCodePageRange2;
+    
+    /* for version >= 0x0002 */
+    S16 sxHeight;
+    S16 sCapHeight;
+    U16 usDefaultChar;
+    U16 usBreakChar;
+    U16 usMaxContext;
+} table_os2_t;
+
+typedef struct _table_hea
+{
+    S16 ascent;
+    S16 descent;
+    S16 lineGap;
+    U16 advanceWidthMax;
+    S16 minLeftSideBearing;
+    S16 minRightSideBearing;
+    S16 xMaxExtent;
+    S16 caretSlopeRise;
+    S16 caretSlopeRun;
+    S16 caretOffset;
+} table_hea_t;
+
+#define GLYPH_ON_CURVE 0x01
+#define GLYPH_CONTOUR_START 0x40
+#define GLYPH_CONTOUR_END 0x80
+typedef struct _ttfpoint {
+    int x,y;
+    U8 flags;
+} ttfpoint_t;
+typedef struct _ttfglyph {
+    U16 advance;
+    U16 bearing;
+    S16 xmin,ymin,xmax,ymax;
+    int code_size;
+    U8*code;
+    int num_points;
+    ttfpoint_t*points;
+} ttfglyph_t;
+
+typedef struct _table_head {
+    U16 flags;
+    U16 units_per_em;
+    S16 xmin,ymin,xmax,ymax;
+    U16 macStyle;
+    U16 lowest_readable_size;
+    S16 dir_hint;
+} table_head_t;
+
+typedef struct _ttf {
+    ttf_table_t*tables;
+
+    table_head_t*head;
+    table_maxp_t*maxp;
+    table_os2_t*os2;
+    table_hea_t*hea;
+
+    U16 flags;
+    char is_vertical;
+    int num_glyphs;
+    ttfglyph_t*glyphs;
+
+    U32 version;
+} ttf_t;
+
+
+ttf_t*load_ttf(void*data, int length);
+ttf_table_t*ttf_addtable(ttf_t*ttf, U32 tag);
+
+#endif