From ea0d9227c96c1e2ba46c3dd3be93fb8ed1322958 Mon Sep 17 00:00:00 2001 From: Matthias Kramm Date: Mon, 15 Feb 2010 20:03:22 -0800 Subject: [PATCH] added truetype parser --- lib/ttf.c | 1400 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/ttf.h | 164 ++++++++ 2 files changed, 1564 insertions(+) create mode 100644 lib/ttf.c create mode 100644 lib/ttf.h diff --git a/lib/ttf.c b/lib/ttf.c new file mode 100644 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 + + 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 +#include +#include +#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->memsizelen+1) + expand(w, w->len+1); + w->data[w->len++] = b; +} +static void writeU16(ttf_table_t*w, unsigned short v) +{ + if(w->memsizelen+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->memsizelen+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->memsizelen+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->idnext) { + after=before; + } + if(before && before->id == id) { + msg(" 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 ",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(" 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(" Font HEAD has unknown version %08x", version); + U32 revision = readU32(r); + if(revision!=VERSION_1_0) + msg(" Font HEAD has unknown revision %08x", revision); + U32 checksum2 = readU32(r); + U32 magic = readU32(r); + if(magic!=0x5f0f3cf5) + msg(" 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(" loca index format %d unknown", loc_index); + U16 glyph_data_format = readS16(r); + if(glyph_data_format!=0) + msg(" 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(" Unknown metric format %d", metricDataFormat); + } + int num_advances = readU16(r); + if(num_advances > ttf->num_glyphs) { + msg(" 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;tglyphs[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;tglyphs[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;tglyphs[t].advance); + writeU16(w, ttf->glyphs[t].bearing); + } + for(;tnum_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(" Short 'loca' table (32 bit)"); + num=r->size/4; + } + if(num*4 < r->size) { + msg(" Extraneous data (%d bytes) in 'loca' table (32 bit)", r->size-num*4); + } + for(t=0;t r->size) { + msg(" Short 'loca' table (16 bit)"); + num=r->size/2; + } + if(num*2 < r->size) { + msg(" Extraneous data (%d bytes) in 'loca' table (16 bit)", r->size-num*2); + } + for(t=0;tnum_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 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(" 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(numnum_points) { + U8 flag = readU8(r); + if(flag&0xc0) { + msg(" 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(" 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(numnum_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_pospoints[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(numnum_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;tnum_glyphs;t++) { + INIT_READ(r, rr->mem, rr->size, loca[t]); + if(r.pos+10>r.size) { + msg(" 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(" 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;snum_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;snum_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;snum_points;s++) { + ttfpoint_t*p = &g->points[s]; + int dx = p->x - lastx; + if(dx>32767 || dx<-32768) { + msg(" 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;snum_points;s++) { + ttfpoint_t*p = &g->points[s]; + int dy = p->y - lasty; + if(dy>32767 || dy<-32768) { + msg(" 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;tnum_glyphs;t++) { + locations[t] = w->len; + ttfglyph_t*g = &ttf->glyphs[t]; + int s; + int num_contours = 0; + for(s=0;snum_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;tnum_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;snum_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;tnum_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(" 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(" 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(" 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(" 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(" 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(" 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(" 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(" 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 length) { + msg(" 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(" 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(" Truetype file version %08x%s", ttf->version, ttf->version == OPENTYPE?" (opentype)":""); + ttf_table_t*table = ttf->tables; + while(table) { + U32 tag = table->id; + msg(" 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(" 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 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 + + 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 -- 1.7.10.4