X-Git-Url: http://git.asbjorn.biz/?p=swftools.git;a=blobdiff_plain;f=lib%2Fttf.c;h=c1fd06db7be04816483718cfe5020d941697c6c8;hp=c637f6e615d42b1f520d303f2fed6779c77051ab;hb=fc8854e5ed42dbcef96d25eb851b9873707a15ff;hpb=f4d875b670dfcdfd8b62c396c88098dc1ba7f8d8 diff --git a/lib/ttf.c b/lib/ttf.c index c637f6e..c1fd06d 100644 --- a/lib/ttf.c +++ b/lib/ttf.c @@ -22,9 +22,10 @@ #include #include #include -#include "bitio.h" +#include #include "log.h" #include "os.h" +#include "q.h" #include "mem.h" #include "ttf.h" @@ -48,13 +49,11 @@ #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) { @@ -117,6 +116,10 @@ static void readBlock(memreader_t*r, void*dest, int len) r->pos += len; } } +static void reader_reset(memreader_t*r) +{ + r->pos; +} #define INIT_READ(r,data,length,pos) memreader_t r = {(data),(pos),(length)}; static void expand(ttf_table_t*w, int newsize) @@ -126,21 +129,28 @@ static void expand(ttf_table_t*w, int newsize) w->memsize = v1>v2?v1:v2; w->data = rfx_realloc(w->data, w->memsize); } -static void writeU8(ttf_table_t*w, unsigned char b) +static inline 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) +static inline 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; } +static inline void writeU16_LE(ttf_table_t*w, unsigned short v) +{ + if(w->memsizelen+2) + expand(w, w->len+2); + w->data[w->len++] = v; + w->data[w->len++] = v>>8; +} #define writeS16 writeU16 -static void writeU32(ttf_table_t*w, unsigned long v) +static inline void writeU32(ttf_table_t*w, unsigned long v) { if(w->memsizelen+4) expand(w, w->len+4); @@ -149,7 +159,16 @@ static void writeU32(ttf_table_t*w, unsigned long v) w->data[w->len++] = v>>8; w->data[w->len++] = v; } -static void writeBlock(ttf_table_t*w, void*data, int len) +static inline void writeU32_LE(ttf_table_t*w, unsigned long v) +{ + if(w->memsizelen+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->memsizelen+len) expand(w, w->len+len); @@ -179,7 +198,7 @@ ttf_table_t*ttf_addtable(ttf_t*ttf, U32 id) before->len = 0; return before; } - + if(!after) { t->next = ttf->tables; ttf->tables = t; @@ -196,7 +215,7 @@ ttf_table_t*ttf_find_table(ttf_t*ttf, U32 id) { ttf_table_t*table = ttf->tables; while(table) { - if(table->id == id) + if(table->id == id) return table; table = table->next; } @@ -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(table->prev) + if(table->prev) table->prev->next = table->next; - if(table->next) + if(table->next) table->next->prev = table->prev; free(table->data); free(table); @@ -258,16 +277,182 @@ static void ttf_table_dump(ttf_table_t*t, const char*prefix) hexdump(t->data, t->len, prefix); } +static table_head_t*head_new(ttf_t*ttf) +{ + table_head_t*head = rfx_calloc(sizeof(table_head_t)); + head->units_per_em = 1024; + int t; + if(ttf->num_glyphs) { + head->xmin = ttf->glyphs[0].xmin; + head->ymin = ttf->glyphs[0].ymin; + head->xmax = ttf->glyphs[0].xmax; + head->ymax = ttf->glyphs[0].ymax; + for(t=1;tnum_glyphs;t++) { + if(ttf->glyphs[t].xmin < head->xmin) head->xmin = ttf->glyphs[t].xmin; + if(ttf->glyphs[t].ymin < head->ymin) head->ymin = ttf->glyphs[t].ymin; + if(ttf->glyphs[t].xmax > head->xmax) head->xmax = ttf->glyphs[t].xmax; + if(ttf->glyphs[t].ymax > head->ymax) head->ymax = ttf->glyphs[t].ymax; + } + } + head->macStyle = 0; + head->lowest_readable_size = 8; // not sure what font renderers actually do with this + head->dir_hint = 0; + return head; +} +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); + 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); + if(r->pos < r->size) { + msg(" Leftover bytes (%d) in HEAD tag", r->size - r->pos); + } + 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_os2_t*os2_new(ttf_t*ttf) +{ + table_os2_t*os2 = rfx_calloc(sizeof(table_os2_t)); + if(ttf->num_glyphs) { + int average_width=0; + int t; + for(t=0;tnum_glyphs;t++) { + average_width += (ttf->glyphs[t].advance + ttf->glyphs[t].bearing); + } + os2->xAvgCharWidth = average_width / ttf->num_glyphs; + } + + /* 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); + int ymid = height/2; + /* I do believe a sane font rendering engine will actually use + the font advance here- the subscript/superscript position will + not be the same for each glyph */ + os2->ySuperscriptXSize = os2->ySubscriptXSize = (ttf->head->xmax - ttf->head->xmin)/2; + os2->ySuperscriptYSize = os2->ySubscriptYSize = (ttf->head->ymax - ttf->head->ymin)/2; + os2->ySubscriptXOffset = advance; + os2->ySubscriptYOffset = 0; + os2->ySuperscriptXOffset = advance; + os2->ySuperscriptYOffset = (ttf->head->ymax - ttf->head->ymin)/2; + os2->yStrikeoutSize = ttf->head->units_per_em / 10; + os2->yStrikeoutPosition = ymid; + os2->usWinAscent = ttf->ascent; + os2->usWinDescent = ttf->descent>0?0:-ttf->descent; + os2->sxHeight = ymid; + os2->sCapHeight = height*2/3; + } + os2->panose_Weight = 4; + + /* strictly speaking we'd have to set 92/64 bits in these tables, depending on + what parts of the unicode table is filled. (e.g. bit 90 = tibetan). */ + os2->ulCharRange[0] = 1; + os2->ulCharRange[1] = 0; + os2->ulCharRange[2] = 0; + os2->ulCharRange[3] = 0; + os2->ulCodePageRange1 = 1; + os2->ulCodePageRange2 = 0; + + if(ttf->unicode_size) { + int min,max; + for(min=0;minunicode_size;min++) + if(ttf->unicode[min]) break; + for(max=ttf->unicode_size-1;max>=0;max--) + if(ttf->unicode[max]) break; + if(min<=max) { + os2->fsFirstCharIndex = min; + os2->fsLastCharIndex = max; + } + } + os2->sTypoAscender = ttf->ascent; + os2->sTypoDescender = ttf->descent; + os2->sTypoLineGap = ttf->lineGap; + + os2->usDefaultChar = 0; + os2->usBreakChar = (ttf->unicode_size>0x20 && ttf->unicode[0x20])?0x20:0; + os2->usMaxContext = 0; // we don't use ligatures yet + return os2; +} 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) + /* 0 = TrueType 1.5 + 1 = TrueType 1.66 + 2 = OpenType 1.2 + 3 = OpenType 1.4 */ + if(version!=0 && version!=1 && version!=2 && version!=3) msg(" Unknown OS2 version: %04x", version); os2->xAvgCharWidth = readS16(r); os2->usWeightClass = readU16(r); os2->usWidthClass = readU16(r); - os2->fsType = readU16(r); + readU16(r); //fstype os2->ySubscriptXSize = readU16(r); os2->ySubscriptYSize = readU16(r); os2->ySubscriptXOffset = readU16(r); @@ -293,10 +478,7 @@ static table_os2_t*os2_parse(memreader_t*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); + readU32(r); //vendor os2->fsSelection = readU16(r); os2->fsFirstCharIndex = readU16(r); os2->fsLastCharIndex = readU16(r); @@ -314,10 +496,13 @@ static table_os2_t*os2_parse(memreader_t*r) os2->usDefaultChar = readU16(r); os2->usBreakChar = readU16(r); os2->usMaxContext = readU16(r); + + if(r->pos < r->size) { + msg(" Leftover bytes (%d) in OS2 tag", r->size - r->pos); + } return os2; } - -static os2_write(ttf_t*ttf, ttf_table_t*w) +static void os2_write(ttf_t*ttf, ttf_table_t*w) { table_os2_t*os2 = ttf->os2; U16 version=1; @@ -328,7 +513,7 @@ static os2_write(ttf_t*ttf, ttf_table_t*w) writeS16(w, os2->xAvgCharWidth); writeU16(w, os2->usWeightClass); writeU16(w, os2->usWidthClass); - writeU16(w, os2->fsType); + writeU16(w, 0); //fstype writeU16(w, os2->ySubscriptXSize); writeU16(w, os2->ySubscriptYSize); writeU16(w, os2->ySubscriptXOffset); @@ -354,10 +539,7 @@ static os2_write(ttf_t*ttf, ttf_table_t*w) 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]); + writeU32(w, 0x53434244); //vendor writeU16(w, os2->fsSelection); writeU16(w, os2->fsFirstCharIndex); writeU16(w, os2->fsLastCharIndex); @@ -376,21 +558,13 @@ static os2_write(ttf_t*ttf, ttf_table_t*w) 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) +static void 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); @@ -416,10 +590,6 @@ static os2_dump(ttf_t*ttf) 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); @@ -436,88 +606,52 @@ static os2_dump(ttf_t*ttf) printf("os2->usBreakChar: %d\n", os2->usBreakChar); printf("os2->usMaxContext: %d\n", os2->usMaxContext); } - -static int head_parse(ttf_t*ttf, memreader_t*r) +static void os2_delete(ttf_t*ttf) { - 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; + if(ttf->os2) + free(ttf->os2); + ttf->os2=0; } -static void head_write(ttf_t*ttf, ttf_table_t*w, int loca_size) +static table_maxp_t*maxp_new(ttf_t*ttf) { - 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; + table_maxp_t*maxp = rfx_calloc(sizeof(table_maxp_t)); + int t; + maxp->maxContours=1; + if(ttf->num_glyphs) { + int max = 1; + for(t=0;tnum_glyphs;t++) { + if(ttf->glyphs[t].num_points>max) + max = ttf->glyphs[t].num_points; + int contours = 0; + int s; + for(s=0;sglyphs[t].num_points;s++) { + if(ttf->glyphs[t].points[s].flags&GLYPH_CONTOUR_END) + contours++; + } + if(maxp->maxContours < contours) + maxp->maxContours = contours; + } + maxp->maxPoints = max; + + /* we don't generate composite glyphs yet */ + maxp->maxComponentPoints = 0; + maxp->maxComponentContours = 0; } + maxp->maxZones = 2; // we don't use the Z0 zone + return maxp; } - static table_maxp_t* maxp_parse(ttf_t*ttf, memreader_t*r) { U32 version = readU32(r); ttf->num_glyphs = readU16(r); - /* according to freetype, older fonts (version<0x10000) + /* according to freetype, older fonts (version<0x10000) apparently only contain the number of glyphs. this is rather rare, though. */ - if(version<0x10000) return 0; + if(version<0x10000 && r->size==6) return 0; + + if(r->size<32) + msg(" Truncated maxp table (version %d)", version); table_maxp_t*maxp = rfx_calloc(sizeof(table_maxp_t)); maxp->maxPoints = readU16(r); @@ -535,10 +669,15 @@ static table_maxp_t* maxp_parse(ttf_t*ttf, memreader_t*r) maxp->maxComponentDepth = readU16(r); return maxp; } - static void maxp_write(ttf_t*ttf, ttf_table_t*w) { table_maxp_t*maxp = ttf->maxp; + if(!maxp) { + /* version 0.5 simplified maxp table */ + writeU32(w, 0x00005000); + writeU16(w, ttf->num_glyphs); + return; + } writeU32(w, 0x10000); //version writeU16(w, ttf->num_glyphs); writeU16(w, maxp->maxPoints); @@ -555,7 +694,6 @@ static void maxp_write(ttf_t*ttf, ttf_table_t*w) writeU16(w, maxp->maxComponentElements); writeU16(w, maxp->maxComponentDepth); } - static void maxp_dump(ttf_t*ttf) { table_maxp_t*maxp = ttf->maxp; @@ -574,7 +712,6 @@ static void maxp_dump(ttf_t*ttf) printf("maxp->maxComponentElements: %d\n", maxp->maxComponentElements); printf("maxp->maxComponentDepth: %d\n", maxp->maxComponentDepth); } - static void maxp_delete(ttf_t*ttf) { if(ttf->maxp) @@ -582,14 +719,33 @@ static void maxp_delete(ttf_t*ttf) ttf->maxp=0; } - +static table_hea_t*hea_new(ttf_t*ttf) +{ + table_hea_t*hea = rfx_calloc(sizeof(table_hea_t)); + if(ttf->num_glyphs) { + int t; + for(t=0;tnum_glyphs;t++) { + if(ttf->glyphs[t].advance > hea->advanceWidthMax) + hea->advanceWidthMax = ttf->glyphs[t].advance; + 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; + } + hea->caretSlopeRise = 1; + } + return hea; +} 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); + ttf->ascent = readS16(r); + ttf->descent = readS16(r); + ttf->lineGap = readS16(r); hea->advanceWidthMax = readU16(r); hea->minLeftSideBearing = readS16(r); hea->minRightSideBearing = readS16(r); @@ -612,14 +768,13 @@ static int hea_parse(memreader_t*r, ttf_t*ttf) } 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); + writeS16(w, ttf->ascent); + writeS16(w, ttf->descent); + writeS16(w, ttf->lineGap); writeU16(w, hea->advanceWidthMax); writeS16(w, hea->minLeftSideBearing); writeS16(w, hea->minRightSideBearing); @@ -638,10 +793,11 @@ static table_hea_t*hea_write(ttf_t*ttf, ttf_table_t*w, int num_advances) static void hea_dump(ttf_t*ttf) { table_hea_t*hea = ttf->hea; + if(!hea) return; 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->ascent: %d\n", dir, ttf->ascent); + printf("%shea->descent: %d\n", dir, ttf->descent); + printf("%shea->lineGap: %d\n", dir, ttf->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); @@ -658,7 +814,7 @@ static void hea_delete(ttf_t*ttf) } } -static void hmtx_parse(memreader_t*r, ttf_t*ttf, int num_advances) +static void mtx_parse(memreader_t*r, ttf_t*ttf, int num_advances) { U16 old_advance = 0; int t; @@ -673,7 +829,7 @@ static void hmtx_parse(memreader_t*r, ttf_t*ttf, int num_advances) rest = ttf->num_glyphs-num_advances; } for(t=0;tglyphs[t].advance = old_advance; + ttf->glyphs[t].advance = old_advance; ttf->glyphs[t].bearing = readS16(r); } } @@ -694,28 +850,37 @@ static int mtx_write(ttf_t*ttf, ttf_table_t*w) int t; for(t=0;tglyphs[t].advance); - writeU16(w, ttf->glyphs[t].bearing); + writeS16(w, ttf->glyphs[t].bearing); } for(;tnum_glyphs;t++) { - writeU16(w, ttf->glyphs[t].bearing); + writeS16(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)); + U32 lastloc = 0; + U32 loc = 0; + char warn_unsorted = 1; if(size) { if(num*4 > r->size) { - msg(" Short 'loca' table (32 bit)"); + msg(" Short 'loca' table (32 bit): %d/%d", r->size/4, num); 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 loc && warn_unsorted) { + msg(" Unsorted 'loca' table (32 bit)"); + warn_unsorted=0; + } + lastloc = loc; } } else { if(num*2 > r->size) { @@ -726,7 +891,12 @@ static U32*loca_parse(memreader_t*r, ttf_t*ttf, int size) msg(" Extraneous data (%d bytes) in 'loca' table (16 bit)", r->size-num*2); } for(t=0;t loc && warn_unsorted) { + msg(" Unsorted 'loca' table"); + warn_unsorted=0; + } + lastloc = loc; } } return locations; @@ -754,6 +924,7 @@ static int loca_write(ttf_t*ttf, ttf_table_t*w, U32*locations) return 0; } } + static int parse_simple_glyph(ttf_t*ttf, memreader_t*r, int num_contours, int glyphnr) { ttfglyph_t*glyph = &ttf->glyphs[glyphnr]; @@ -778,15 +949,15 @@ static int parse_simple_glyph(ttf_t*ttf, memreader_t*r, int num_contours, int gl glyph->code_size = code_len; } - if(!endpoints) + if(!endpoints) return 1; - /*msg(" TTF Glyph %d) code_size=%d num_contours=%d glyph->num_points=%d %d/%d/%d/%d", + /*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); @@ -802,7 +973,7 @@ static int parse_simple_glyph(ttf_t*ttf, memreader_t*r, int num_contours, int gl return 0; } int count = 1; - if(flag & 0x08) + if(flag & 0x08) count += readU8(r); if(count+num>glyph->num_points) { msg(" Bad count (%d) in glyph (%d) (at pos %d)", count, glyphnr, num); @@ -831,7 +1002,7 @@ static int parse_simple_glyph(ttf_t*ttf, memreader_t*r, int num_contours, int gl if((flag&0x12) == 0x12) x += readU8(r); else if((flag&0x12) == 0x02) x -= readU8(r); else if((flag&0x12) == 0x00) x += readS16(r); - + glyph->points[num].x = x; U8 f = flag&GLYPH_ON_CURVE; if(is_start) f|=GLYPH_CONTOUR_START; @@ -841,7 +1012,7 @@ static int parse_simple_glyph(ttf_t*ttf, memreader_t*r, int num_contours, int gl is_start = is_end; } while(--count); } - + /* parse flag array (3rd pass) and y coordinates */ num=0; int y = 0; @@ -863,11 +1034,14 @@ static int parse_simple_glyph(ttf_t*ttf, memreader_t*r, int num_contours, int gl static void glyf_parse(memreader_t*rr, ttf_t*ttf, U32*loca) { int t; - char warn_about_compound_glyphs=1; + char warn_about_compound_glyphs=0; for(t=0;tnum_glyphs;t++) { INIT_READ(r, rr->mem, rr->size, loca[t]); + if(loca[t]==loca[t+1] || loca[t]==r.size) + continue; //empty glyph if(r.pos+10>r.size) { - msg(" Unexpected end of glyph array (or bad loca entry %d/%d)", loca[t], r.size); + msg(" 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; } S16 num_contours = readS16(&r); @@ -875,7 +1049,7 @@ static void glyf_parse(memreader_t*rr, ttf_t*ttf, U32*loca) ttf->glyphs[t].ymin = readS16(&r); ttf->glyphs[t].xmax = readS16(&r); ttf->glyphs[t].ymax = readS16(&r); - + if(num_contours<0) { if(warn_about_compound_glyphs) msg(" Compound glyphs not supported yet"); @@ -887,7 +1061,6 @@ static void glyf_parse(memreader_t*rr, ttf_t*ttf, U32*loca) } } - void write_simple_glyph(ttf_table_t*w, ttfglyph_t*g) { /* endpoints array */ @@ -896,7 +1069,7 @@ void write_simple_glyph(ttf_table_t*w, ttfglyph_t*g) if(g->points[s].flags&GLYPH_CONTOUR_END) writeU16(w, s); } - + /* bytecode */ writeU16(w, g->code_size); if(g->code_size) @@ -979,7 +1152,6 @@ void write_simple_glyph(ttf_table_t*w, ttfglyph_t*g) 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)); @@ -998,7 +1170,7 @@ U32* glyf_write(ttf_t* ttf, ttf_table_t*w) writeS16(w, g->ymin); writeS16(w, g->xmax); writeS16(w, g->ymax); - + if(!num_contours) { /* some ttf parsers can't deal with zero contours, so in the case of an empty glyph, write a single point (0,0) */ @@ -1016,6 +1188,7 @@ U32* glyf_write(ttf_t* ttf, ttf_table_t*w) } void glyf_dump(ttf_t* ttf) { + if(!ttf->glyphs) return; int t; for(t=0;tnum_glyphs;t++) { ttfglyph_t*g = &ttf->glyphs[t]; @@ -1030,14 +1203,13 @@ void glyf_dump(ttf_t* ttf) printf("%d/%d/0x%02x", g->points[s].x, g->points[s].y, g->points[s].flags); } printf(")\n"); - if(g->code_size) + if(g->code_size) hexdump(g->code, g->code_size, " "); } } - void glyf_delete(ttf_t* ttf) { - if(!ttf->glyphs) + if(!ttf->glyphs) return; int t; for(t=0;tnum_glyphs;t++) { @@ -1064,12 +1236,12 @@ static void grow_unicode(ttf_t*ttf, int index) } ttf->unicode_size = size; } - void cmap_parse(memreader_t*r, ttf_t*ttf) { readU16(r); // version (0) int num_subtables = readU16(r); int t; + char warn=1; if(r->pos+num_subtables*8 > r->size) { msg(" CMap overflow"); num_subtables = (r->size-r->pos)/8; @@ -1088,7 +1260,7 @@ void cmap_parse(memreader_t*r, ttf_t*ttf) platform==3 && encoding == 1 || platform==3 && encoding == 10; - if(!is_unicode) + if(!is_unicode) continue; INIT_READ(t, r->mem, r->size, offset); @@ -1113,7 +1285,7 @@ void cmap_parse(memreader_t*r, ttf_t*ttf) ttf->unicode[s] = readU8(&t); } } else if(format == 4) { - U16 segment_count = readU16(&t); + U16 segment_count = readU16(&t); if(segment_count&1) { msg(" Bad segmentx2 count %d", segment_count); continue; @@ -1127,6 +1299,7 @@ void cmap_parse(memreader_t*r, ttf_t*ttf) INIT_READ(r_delta, t.mem, t.size, t.pos+2+segment_count*4); INIT_READ(r_range, t.mem, t.size, t.pos+2+segment_count*6); int glyphmap_start = t.pos+2+segment_count*8; + int glyphmap_size = t.size - glyphmap_start; int s; for(s=0;sunicode[u] = (u + delta) & 0xffff; } } else { - INIT_READ(g, t.mem, t.size, r_range.pos-2+range); + int pos = r_range.pos-2+range; + if(warn && pos+end-start+1 > t.size) { + msg(" glyphmap index out of bounds (%d-%d/%d)", pos, pos+end-start, t.size); + warn=0; + } + INIT_READ(g, t.mem, t.size, pos); for(u=start;u<=end;u++) { ttf->unicode[u] = readU16(&g); } @@ -1157,7 +1336,6 @@ void cmap_parse(memreader_t*r, ttf_t*ttf) } } } - static int segment_size(unicode_t*unicode, int pos, int size) { int s; @@ -1166,7 +1344,7 @@ static int segment_size(unicode_t*unicode, int pos, int size) if(!unicode[s]) count++; if(count>4) { - /* a segment costs us 8 bytes, so for more than 4 consecutive + /* a segment costs us 8 bytes, so for more than 4 consecutive zero entries (16 bit each) in the glyph index array, it pays off to start a new segment */ break; @@ -1177,7 +1355,6 @@ static int segment_size(unicode_t*unicode, int pos, int size) return size-1; return s; } - void cmap_write(ttf_t* ttf, ttf_table_t*w) { writeU16(w, 0); //version @@ -1212,6 +1389,11 @@ void cmap_write(ttf_t* ttf, ttf_table_t*w) pos = s+1; num_segments++; } + + 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;tlen; for(t=0;tdata[num_segments_pos]=num_segments>>8; - w->data[num_segments_pos+1]=num_segments; + + /* 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; @@ -1242,7 +1444,7 @@ void cmap_write(ttf_t* ttf, ttf_table_t*w) U16 delta = ttf->unicode[pos]-pos; char do_delta=1; for(s=pos+1;s<=end;s++) { - U16 delta2 = ttf->unicode[pos]-pos; + U16 delta2 = ttf->unicode[s]-s; if(delta2!=delta) { do_delta=0; break; @@ -1253,7 +1455,7 @@ void cmap_write(ttf_t* ttf, ttf_table_t*w) range = 0; } else { delta = 0; - range = w->len - range_pos+num_segments*2; + range = w->len - range_pos; for(s=pos;s<=end;s++) { writeU16(w, ttf->unicode[s]); } @@ -1265,6 +1467,18 @@ void cmap_write(ttf_t* ttf, ttf_table_t*w) num_segments++; pos = end+1; } + + /* write out a mapping from 0xffff to 0- seems to be required + by some libraries (e.g. fonttools) */ + w->data[end_pos++]=0xff; + w->data[end_pos++]=0xff; + w->data[start_pos++]=0xff; + w->data[start_pos++]=0xff; + w->data[delta_pos++]=0; + w->data[delta_pos++]=1; + w->data[range_pos++]=0; + w->data[range_pos++]=0; + w->data[length_pos]=(w->len-20)>>8; w->data[length_pos+1]=w->len-20; } @@ -1276,6 +1490,330 @@ void cmap_delete(ttf_t*ttf) } ttf->unicode_size=0; } +static char*readString(memreader_t*r, int len) +{ + char*s = malloc(len+1); + readBlock(r, s, len); + s[len] = 0; + return s; +} +void name_parse(memreader_t*r, ttf_t*ttf) +{ + U16 format = readU16(r); + U16 count = readU16(r); + U16 offset = readU16(r); + + int t; + for(t=0;tmem, r->size, offset+offset_2); + if(!(platform==0 || (platform==1 && encoding==0))) + continue; + + INIT_READ(s, r->mem, r->size, offset+offset_2); + + switch (name_id) { + case 1: read_name = &ttf->family_name; break; + case 2: read_name = &ttf->subfamily_name; break; + case 3: read_name = &ttf->font_uid; break; + case 4: read_name = &ttf->full_name; break; + case 5: read_name = &ttf->version_string; break; + case 6: read_name = &ttf->postscript_name; break; + default: read_name = 0; + } + + if (read_name) { + if (*read_name) free(*read_name); + *read_name = readString(&s, len); + } + } +} +void name_write(ttf_t*ttf, ttf_table_t*table) +{ + char*strings[6] = {ttf->family_name, ttf->subfamily_name, ttf->font_uid, ttf->full_name, ttf->version_string, ttf->postscript_name}; + int codes[6] = {1,2,3,4,5,6}; + + writeU16(table, 0); //format + int count = 0; + int t; + int nr = sizeof(strings)/sizeof(strings[0]); + + for(t=0;tlen; + 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;tdata[offset_pos] = table->len>>8; + table->data[offset_pos+1] = table->len; + + for(t=0;tfull_name) { + free(ttf->full_name); + ttf->full_name=0; + } + if(ttf->family_name) { + free(ttf->family_name); + ttf->family_name=0; + } + if(ttf->subfamily_name) { + free(ttf->subfamily_name); + ttf->subfamily_name=0; + } + if(ttf->version_string) { + free(ttf->version_string); + ttf->version_string=0; + } + if(ttf->font_uid) { + free(ttf->font_uid); + ttf->font_uid=0; + } + if(ttf->postscript_name) { + free(ttf->postscript_name); + ttf->postscript_name=0; + } +} + +static table_post_t*post_new(ttf_t*ttf) +{ + table_post_t*post = rfx_calloc(sizeof(table_post_t)); + return post; +} +void post_parse(memreader_t*r, ttf_t*ttf) +{ + table_post_t*post = ttf->post = rfx_calloc(sizeof(table_post_t)); + U32 format = readU32(r); + post->italic_angle = readU32(r); + post->underline_position = readU16(r); + post->underline_thickness = readU16(r); + U16 is_monospaced = readU32(r); + readU32(r); // min mem 42 + readU32(r); + readU32(r); // min mem 1 + readU32(r); +} +void post_write(ttf_t*ttf, ttf_table_t*table) +{ + table_post_t*post = ttf->post; + writeU32(table, 0x00030000); + writeU32(table, post->italic_angle); + writeU16(table, post->underline_position); + writeU16(table, post->underline_thickness); + writeU32(table, 0); //is monospaced TODO + writeU32(table, 0); //min mem 42 + writeU32(table, 0); + writeU32(table, 0); //min mem 1 + writeU32(table, 0); +} +void post_delete(ttf_t*ttf) +{ + if(ttf->post) { + free(ttf->post); + ttf->post = 0; + } +} + +void cvt_parse(memreader_t*r, ttf_t*ttf) +{ + table_cvt_t*cvt = ttf->cvt = rfx_calloc(sizeof(table_cvt_t)); + cvt->num = r->size/2; + cvt->values = malloc(cvt->num*sizeof(S16)); + int t; + for(t=0;tnum;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;tnum;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;trecords[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;tnum;t++) { + if(gasp->records[t].behaviour & ~(GASP_GRIDFIT | GASP_DOGRAY)) { + version = 1; + } + } + writeU16(table, version); + writeU16(table, gasp->num); + for(t=0;tnum;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) { @@ -1289,7 +1827,7 @@ static int ttf_parse_tables(ttf_t*ttf) INIT_READ(m, table->data, table->len, 0); int loc_index = head_parse(ttf, &m); ttf_table_delete(ttf, table); - + table = ttf_find_table(ttf, TAG_MAXP); if(!table) { msg(" Font has no maxp table"); @@ -1298,7 +1836,7 @@ static int ttf_parse_tables(ttf_t*ttf) INIT_READ(m2, table->data, table->len, 0); ttf->maxp = maxp_parse(ttf, &m2); ttf_table_delete(ttf, table); - + if(!ttf->num_glyphs) { msg(" Invalid number of characters"); return 0; @@ -1312,16 +1850,17 @@ static int ttf_parse_tables(ttf_t*ttf) ttf_table_delete(ttf, table); } + table = ttf_find_table(ttf, TAG_HHEA); if(table) { INIT_READ(m, table->data, table->len, 0); int num_advances = hea_parse(&m, ttf); ttf_table_delete(ttf, table); - + table = ttf_find_table(ttf, TAG_HMTX); if(table) { INIT_READ(m, table->data, table->len, 0); - hmtx_parse(&m, ttf, num_advances); + mtx_parse(&m, ttf, num_advances); ttf_table_delete(ttf, table); } } else { @@ -1335,7 +1874,7 @@ static int ttf_parse_tables(ttf_t*ttf) table = ttf_find_table(ttf, TAG_VMTX); if(table) { INIT_READ(m, table->data, table->len, 0); - hmtx_parse(&m, ttf, num_advances); + mtx_parse(&m, ttf, num_advances); ttf_table_delete(ttf, table); } } else { @@ -1355,7 +1894,7 @@ static int ttf_parse_tables(ttf_t*ttf) } free(loca); } - + table = ttf_find_table(ttf, TAG_CMAP); if(table) { INIT_READ(m, table->data, table->len, 0); @@ -1363,53 +1902,147 @@ static int ttf_parse_tables(ttf_t*ttf) ttf_table_delete(ttf, table); } + table = ttf_find_table(ttf, TAG_POST); + if(table) { + INIT_READ(m, table->data, table->len, 0); + post_parse(&m, ttf); + ttf_table_delete(ttf, table); + } + + table = ttf_find_table(ttf, TAG_NAME); + if(table) { + INIT_READ(m, table->data, table->len, 0); + name_parse(&m, ttf); + ttf_table_delete(ttf, table); + } + + table = ttf_find_table(ttf, TAG_CVT); + if(table) { + INIT_READ(m, table->data, table->len, 0); + cvt_parse(&m, ttf); + ttf_table_delete(ttf, table); + } + + table = ttf_find_table(ttf, TAG_GASP); + if(table) { + INIT_READ(m, table->data, table->len, 0); + gasp_parse(&m, ttf); + ttf_table_delete(ttf, table); + } + + table = ttf_find_table(ttf, TAG_PREP); + if(table) { + INIT_READ(m, table->data, table->len, 0); + prep_parse(&m, ttf); + ttf_table_delete(ttf, table); + } + + table = ttf_find_table(ttf, TAG_FPGM); + if(table) { + INIT_READ(m, table->data, table->len, 0); + fpgm_parse(&m, ttf); + ttf_table_delete(ttf, table); + } + return 1; } static void ttf_collapse_tables(ttf_t*ttf) { ttf_table_t*table; - - table = ttf_addtable(ttf, TAG_MAXP); - maxp_write(ttf, table); - maxp_delete(ttf); - table = ttf_addtable(ttf, TAG_OS2); - os2_write(ttf, table); - os2_delete(ttf); + ttf_table_t*head = ttf_find_table(ttf, TAG_HEAD); + if(head) + return; //already collapsed - if(!ttf->is_vertical) { - table = ttf_addtable(ttf, TAG_HMTX); - int num_advances = mtx_write(ttf, table); - table = ttf_addtable(ttf, TAG_HHEA); - hea_write(ttf, table, num_advances); - } else { - table = ttf_addtable(ttf, TAG_VMTX); - int num_advances = mtx_write(ttf, table); - table = ttf_addtable(ttf, TAG_VHEA); - hea_write(ttf, table, num_advances); + if(ttf->maxp) { + table = ttf_addtable(ttf, TAG_MAXP); + maxp_write(ttf, table); + maxp_delete(ttf); } - + + if(ttf->os2) { + table = ttf_addtable(ttf, TAG_OS2); + os2_write(ttf, table); + os2_delete(ttf); + } + + if(ttf->hea) { + if(!ttf->is_vertical) { + table = ttf_addtable(ttf, TAG_HMTX); + int num_advances = mtx_write(ttf, table); + table = ttf_addtable(ttf, TAG_HHEA); + hea_write(ttf, table, num_advances); + hea_delete(ttf); + } else { + table = ttf_addtable(ttf, TAG_VMTX); + int num_advances = mtx_write(ttf, table); + table = ttf_addtable(ttf, TAG_VHEA); + hea_write(ttf, table, num_advances); + hea_delete(ttf); + } + } + int loca_size=0; if(ttf->num_glyphs) { - table = ttf_addtable(ttf, TAG_CMAP); - cmap_write(ttf, table); - cmap_delete(ttf); + if(ttf->unicode) { + table = ttf_addtable(ttf, TAG_CMAP); + cmap_write(ttf, table); + cmap_delete(ttf); + } - table = ttf_addtable(ttf, TAG_GLYF); - U32*locations = glyf_write(ttf, table); - glyf_delete(ttf); - - table = ttf_addtable(ttf, TAG_LOCA); - loca_size = loca_write(ttf, table, locations); - free(locations); + if(ttf->glyphs) { + table = ttf_addtable(ttf, TAG_GLYF); + U32*locations = glyf_write(ttf, table); + table = ttf_addtable(ttf, TAG_LOCA); + loca_size = loca_write(ttf, table, locations); + free(locations); + glyf_delete(ttf); + } } - + + if(ttf->full_name || ttf->family_name || ttf->subfamily_name || ttf->font_uid || ttf->postscript_name) { + table = ttf_addtable(ttf, TAG_NAME); + name_write(ttf, table); + name_delete(ttf); + } + if(ttf->post) { + table = ttf_addtable(ttf, TAG_POST); + post_write(ttf, table); + post_delete(ttf); + } + if(ttf->cvt) { + table = ttf_addtable(ttf, TAG_CVT); + cvt_write(ttf, table); + cvt_delete(ttf); + } + if(ttf->gasp) { + table = ttf_addtable(ttf, TAG_GASP); + gasp_write(ttf, table); + gasp_delete(ttf); + } + if(ttf->fpgm) { + table = ttf_addtable(ttf, TAG_FPGM); + fpgm_write(ttf, table); + fpgm_delete(ttf); + } + if(ttf->prep) { + table = ttf_addtable(ttf, TAG_PREP); + prep_write(ttf, table); + prep_delete(ttf); + } + table = ttf_addtable(ttf, TAG_HEAD); head_write(ttf, table, loca_size); head_delete(ttf); } -ttf_t* load_ttf(void*data, int length) +ttf_t*ttf_new() +{ + ttf_t*ttf = rfx_calloc(sizeof(ttf_t)); + ttf->version = VERSION_1_0; + return ttf; +} +ttf_t* ttf_load(void*data, int length) { INIT_READ(r,data,length, 0); @@ -1420,6 +2053,62 @@ ttf_t* load_ttf(void*data, int length) ttf_t*ttf = rfx_calloc(sizeof(ttf_t)); ttf->version = readU32(&r); + if(ttf->version == SWAP32(length)) { + U32 fontDataSize = readU32(&r); + U32 version = readU32(&r); + U32 flags = readU32(&r); + U8 panose[10]; + readBlock(&r, panose, 10); + readU8(&r); //charset + readU8(&r); //italoc + readU32(&r); //weight + readU16(&r); //fstype + U16 magic = readU16(&r); //magicNumber + /* we're being paranoid: it's entirely possible for the font + size to be exactly 0x10000. Only treat this font as eot if + it has the right magic number */ + if(magic == 0x4c50) { + readU32(&r); //unicoderange[0] + readU32(&r); //unicoderange[1] + readU32(&r); //unicoderange[2] + readU32(&r); //unicoderange[3] + readU32(&r); //codepagerange[0] + readU32(&r); //codepagerange[1] + readU32(&r); //checksumadjustment + readU32(&r); //reserved[0] + readU32(&r); //reserved[1] + readU32(&r); //reserved[2] + readU32(&r); //reserved[3] + readU16(&r); //padding + + int nr=0; + for(nr=0;nr<4;nr++) { + int t, len; + /* All of ttf is big-endian. All of ttf? No. One small eot table + of indomitable little-endian... */ + len = readU8(&r); + len |= readU8(&r)<<8; + len /= 2; + for(t=0;t>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 */ @@ -1437,17 +2126,17 @@ ttf_t* load_ttf(void*data, int length) r.pos = font1_position; ttf->version = readU32(&r); } - + int num_tables = readU16(&r); - + readU16(&r); //search range readU16(&r); //entry selector readU16(&r); //range shift if(num_tables*16 > length) { - msg(" Truncated TTC file (table entries: %d)", num_tables); - if(ttf->version != OPENTYPE && - ttf->version != TRUETYPE_MACOS && + msg(" 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; @@ -1464,40 +2153,57 @@ ttf_t* load_ttf(void*data, int length) U32 checksum = table_data[t*4+1]; U32 pos = table_data[t*4+2]; U32 len = table_data[t*4+3]; - + if(pos+len > length) { msg(" TTF Table %02x%02x%02x%02x outside of stream (pos %d)", (tag>>24)&0xff, (tag>>16)&0xff, (tag>>8)&0xff, (tag)&0xff, pos); } else { U8*mem = malloc(len); r.pos = pos; readBlock(&r, mem, len); - + ttf_table_t*table = ttf_addtable(ttf, tag); table->data = mem; table->len = table->memsize = len; - +#if 0 U32 checksum2 = ttf_table_checksum(table); if(checksum2!=checksum) { - msg(" Checksum mismatch in tag %02x%02x%02x%02x %c%c%c%c (%d bytes) %08x!=%08x", + 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); } - +#endif } } free(table_data); - if(!ttf_parse_tables(ttf)) + if(!ttf_parse_tables(ttf)) return 0; return ttf; } +void ttf_create_truetype_tables(ttf_t*ttf) +{ + if(!ttf->head) + ttf->head = head_new(ttf); + if(!ttf->maxp) + ttf->maxp = maxp_new(ttf); + if(!ttf->hea) + ttf->hea = hea_new(ttf); + if(!ttf->os2) + ttf->os2 = os2_new(ttf); + if(!ttf->post) + ttf->post = post_new(ttf); + if(!ttf->gasp) + ttf->gasp = gasp_new(ttf); + if(!ttf->prep) + ttf->prep = prep_new(ttf); +} -ttf_table_t* ttf_write(ttf_t*ttf) +ttf_table_t* ttf_write(ttf_t*ttf, U32*checksum_adjust) { ttf_collapse_tables(ttf); - + ttf_table_t*file = ttf_table_new(0); writeU32(file, VERSION_1_0); @@ -1509,7 +2215,7 @@ ttf_table_t* ttf_write(ttf_t*ttf) t = t->next; } writeU16(file, num_tables); - + /* write search range */ int tmp = num_tables; int search_range = 0; @@ -1555,6 +2261,8 @@ ttf_table_t* ttf_write(ttf_t*ttf) writeBlock(file, zero, (-t->len)&3); //pad } U32 checksum = 0xb1b0afba - ttf_table_checksum(file); + if(checksum_adjust) + *checksum_adjust = checksum; U8*checksum2 = file->data + head_pos + 8; checksum2[0] = checksum>>24; checksum2[1] = checksum>>16; @@ -1563,9 +2271,106 @@ ttf_table_t* ttf_write(ttf_t*ttf) 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;idata; + U32 full_len = eot->len + t->len; + len_data[0] = full_len>>0; + len_data[1] = full_len>>8; + len_data[2] = full_len>>16; + len_data[3] = full_len>>24; + + U8*len_data2 = eot->data+4; + len_data2[0] = t->len>>0; + len_data2[1] = t->len>>8; + len_data2[2] = t->len>>16; + len_data2[3] = t->len>>24; + + U8*checksum_data = eot->data + 60; + checksum_data[0] = checksum_adjust>>0; + checksum_data[1] = checksum_adjust>>8; + checksum_data[2] = checksum_adjust>>16; + checksum_data[3] = checksum_adjust>>24; + + FILE*fi = fopen(filename, "wb"); + if(!fi) { + perror(filename); + return; + } + + fwrite(eot->data, eot->len, 1, fi); + fwrite(t->data, t->len, 1, fi); + fclose(fi); + ttf_table_delete(0, t); + ttf_table_delete(0, eot); +} + void ttf_save(ttf_t*ttf, const char*filename) { - ttf_table_t* t = ttf_write(ttf); + ttf_table_t* t = ttf_write(ttf, 0); FILE*fi = fopen(filename, "wb"); if(!fi) { perror(filename); @@ -1582,8 +2387,8 @@ void ttf_dump(ttf_t*ttf) 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, + 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; } @@ -1595,7 +2400,7 @@ void ttf_dump(ttf_t*ttf) maxp_dump(ttf); glyf_dump(ttf); } -void ttf_destroy(ttf_t*ttf) +void ttf_destroy_tables(ttf_t*ttf) { ttf_table_t*table = ttf->tables; while(table) { @@ -1604,11 +2409,23 @@ void ttf_destroy(ttf_t*ttf) free(table); table = next; } + ttf->tables = 0; +} +void ttf_reduce(ttf_t*ttf) +{ + ttf_destroy_tables(ttf); +} +void ttf_destroy(ttf_t*ttf) +{ + ttf_destroy_tables(ttf); maxp_delete(ttf); os2_delete(ttf); head_delete(ttf); hea_delete(ttf); glyf_delete(ttf); + post_delete(ttf); + cvt_delete(ttf); + name_delete(ttf); free(ttf); } @@ -1617,17 +2434,27 @@ int main(int argn, const char*argv[]) { setConsoleLogging(7); const char*filename = "comic.ttf"; - if(argn>1) + if(argn>1) filename = argv[1]; //msg(" Loading %s", filename); memfile_t*m = memfile_open(filename); - ttf_t*ttf = load_ttf(m->data, m->len); + ttf_t*ttf = ttf_load(m->data, m->len); + + if(!ttf) { + msg(" Couldn't load %s", filename); + return 1; + } + ttf_reduce(ttf); + + ttf_create_truetype_tables(ttf); + if(!ttf) return 1; memfile_close(m); //ttf_dump(ttf); - //printf("os2 version: %04x (%d), maxp size: %d\n", + //printf("os2 version: %04x (%d), maxp size: %d\n", // ttf->os2->version, ttf->os2->size, ttf->maxp->size); - ttf_save(ttf, "comic2.ttf"); + ttf_save_eot(ttf, "testfont.eot"); + ttf_save(ttf, "testfont.ttf"); ttf_destroy(ttf); return 0;