X-Git-Url: http://git.asbjorn.biz/?p=swftools.git;a=blobdiff_plain;f=lib%2Fttf.c;h=a5bcfe4483901118414631c7756a74046a82efd9;hp=c17b6316b1342c22d4304281e93d9ec4af56b78f;hb=114fd977a15dcc7a9b52700279576cba80e6dc09;hpb=4c389168a91bf083e434885fbfd8f51c6dab56fc diff --git a/lib/ttf.c b/lib/ttf.c index c17b631..a5bcfe4 100644 --- a/lib/ttf.c +++ b/lib/ttf.c @@ -22,8 +22,10 @@ #include #include #include +#include #include "log.h" #include "os.h" +#include "q.h" #include "mem.h" #include "ttf.h" @@ -116,6 +118,10 @@ static void readBlock(memreader_t*r, void*dest, int len) r->pos += len; } } +static void reader_reset(memreader_t*r) +{ + r->pos; +} #define INIT_READ(r,data,length,pos) memreader_t r = {(data),(pos),(length)}; static void expand(ttf_table_t*w, int newsize) @@ -138,6 +144,13 @@ static inline void writeU16(ttf_table_t*w, unsigned short v) w->data[w->len++] = v>>8; w->data[w->len++] = v; } +static inline void writeU16_LE(ttf_table_t*w, unsigned short v) +{ + if(w->memsizelen+2) + expand(w, w->len+2); + w->data[w->len++] = v; + w->data[w->len++] = v>>8; +} #define writeS16 writeU16 static inline void writeU32(ttf_table_t*w, unsigned long v) { @@ -148,6 +161,15 @@ static inline void writeU32(ttf_table_t*w, unsigned long v) w->data[w->len++] = v>>8; w->data[w->len++] = v; } +static inline void writeU32_LE(ttf_table_t*w, unsigned long v) +{ + if(w->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) @@ -268,10 +290,10 @@ static table_head_t*head_new(ttf_t*ttf) head->xmax = ttf->glyphs[0].xmax; head->ymax = ttf->glyphs[0].ymax; for(t=1;tnum_glyphs;t++) { - if(ttf->glyphs[0].xmin < head->xmin) head->xmin = ttf->glyphs[0].xmin; - if(ttf->glyphs[0].ymin < head->ymin) head->ymin = ttf->glyphs[0].ymin; - if(ttf->glyphs[0].xmax > head->xmax) head->xmax = ttf->glyphs[0].xmax; - if(ttf->glyphs[0].ymax > head->ymax) head->ymax = ttf->glyphs[0].ymax; + if(ttf->glyphs[t].xmin < head->xmin) head->xmin = ttf->glyphs[t].xmin; + if(ttf->glyphs[t].ymin < head->ymin) head->ymin = ttf->glyphs[t].ymin; + if(ttf->glyphs[t].xmax > head->xmax) head->xmax = ttf->glyphs[t].xmax; + if(ttf->glyphs[t].ymax > head->ymax) head->ymax = ttf->glyphs[t].ymax; } } head->macStyle = 0; @@ -597,8 +619,9 @@ static table_maxp_t*maxp_new(ttf_t*ttf) { table_maxp_t*maxp = rfx_calloc(sizeof(table_maxp_t)); int t; + maxp->maxContours=1; if(ttf->num_glyphs) { - int max = 0; + int max = 1; for(t=0;tnum_glyphs;t++) { if(ttf->glyphs[t].num_points>max) max = ttf->glyphs[t].num_points; @@ -705,10 +728,10 @@ static table_hea_t*hea_new(ttf_t*ttf) for(t=0;tnum_glyphs;t++) { if(ttf->glyphs[t].advance > hea->advanceWidthMax) hea->advanceWidthMax = ttf->glyphs[t].advance; - if(ttf->glyphs[t].xmin < ttf->hea->minLeftSideBearing) - ttf->hea->minLeftSideBearing = ttf->glyphs[t].xmin; - if(ttf->glyphs[t].xmax < ttf->hea->minRightSideBearing) - ttf->hea->minRightSideBearing = ttf->glyphs[t].xmax; + if(ttf->glyphs[t].xmin < hea->minLeftSideBearing) + hea->minLeftSideBearing = ttf->glyphs[t].xmin; + 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; @@ -1219,6 +1242,7 @@ void cmap_parse(memreader_t*r, ttf_t*ttf) readU16(r); // version (0) int num_subtables = readU16(r); int t; + char warn=1; if(r->pos+num_subtables*8 > r->size) { msg(" CMap overflow"); num_subtables = (r->size-r->pos)/8; @@ -1276,6 +1300,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); } @@ -1363,6 +1393,8 @@ void cmap_write(ttf_t* ttf, ttf_table_t*w) num_segments++; // account for 0xffff mapping + int glyphmap_start = w->len+2+num_segments*8; + int t; int end_pos = w->len; for(t=0;tlen; for(t=0;tdata[num_segments_pos]=(num_segments*2)>>8; w->data[num_segments_pos+1]=(num_segments*2); - + pos=0; num_segments = 0; while(pos < ttf->unicode_size) { @@ -1393,7 +1425,7 @@ void cmap_write(ttf_t* ttf, ttf_table_t*w) U16 delta = ttf->unicode[pos]-pos; char do_delta=1; for(s=pos+1;s<=end;s++) { - U16 delta2 = ttf->unicode[pos]-pos; + U16 delta2 = ttf->unicode[s]-s; if(delta2!=delta) { do_delta=0; break; @@ -1404,7 +1436,7 @@ void cmap_write(ttf_t* ttf, ttf_table_t*w) range = 0; } else { delta = 0; - range = w->len - range_pos+num_segments*2; + range = w->len - range_pos; for(s=pos;s<=end;s++) { writeU16(w, ttf->unicode[s]); } @@ -1439,6 +1471,152 @@ 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); + + if(name_id==1) { + if(ttf->family_name) free(ttf->family_name); + ttf->family_name = readString(&s, len); + } + if(name_id==2) { + if(ttf->subfamily_name) free(ttf->subfamily_name); + ttf->subfamily_name = readString(&s, len); + } + if(name_id==3) { + if(ttf->version_string) free(ttf->version_string); + ttf->version_string = readString(&s, len); + } + if(name_id==4) { + if(ttf->full_name) free(ttf->full_name); + ttf->full_name = readString(&s, len); + } + } +} +void name_write(ttf_t*ttf, ttf_table_t*table) +{ + char*strings[4] = {ttf->full_name, ttf->family_name, ttf->subfamily_name, ttf->version_string}; + int codes[4] = {4,1,2,3}; + + 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) + + 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; + } +} + +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)); + U16 format = readU16(r); + post->italic_angle = readU16(r); + post->underline_position = readU16(r); + post->underline_thickness = readU16(r); + U16 is_monospaced = readU16(r); + readU16(r); // min mem 42 + readU16(r); + readU16(r); // min mem 1 + readU16(r); +} +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; + } +} static int ttf_parse_tables(ttf_t*ttf) { @@ -1525,46 +1703,86 @@ static int ttf_parse_tables(ttf_t*ttf) cmap_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_POST); + if(table) { + INIT_READ(m, table->data, table->len, 0); + post_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); + + ttf_table_t*head = ttf_find_table(ttf, TAG_HEAD); + if(head) + return; //already collapsed + + if(ttf->maxp) { + 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->os2) { + 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); + 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) { + 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); } table = ttf_addtable(ttf, TAG_HEAD); @@ -1589,6 +1807,62 @@ ttf_t* ttf_load(void*data, int length) ttf_t*ttf = rfx_calloc(sizeof(ttf_t)); ttf->version = readU32(&r); + if(ttf->version == SWAP32(length)) { + U32 fontDataSize = readU32(&r); + U32 version = readU32(&r); + U32 flags = readU32(&r); + U8 panose[10]; + readBlock(&r, panose, 10); + readU8(&r); //charset + readU8(&r); //italoc + readU32(&r); //weight + readU16(&r); //fstype + U16 magic = readU16(&r); //magicNumber + /* we're being paranoid: it's entirely possible for the font + size to be exactly 0x10000. Only treat this font as eot if + it has the right magic number */ + if(magic == 0x4c50) { + readU32(&r); //unicoderange[0] + readU32(&r); //unicoderange[1] + readU32(&r); //unicoderange[2] + readU32(&r); //unicoderange[3] + readU32(&r); //codepagerange[0] + readU32(&r); //codepagerange[1] + readU32(&r); //checksumadjustment + readU32(&r); //reserved[0] + readU32(&r); //reserved[1] + readU32(&r); //reserved[2] + readU32(&r); //reserved[3] + readU16(&r); //padding + + int nr=0; + for(nr=0;nr<4;nr++) { + int t, len; + /* All of ttf is big-endian. All of ttf? No. One small eot table + of indomitable little-endian... */ + len = readU8(&r); + len |= readU8(&r)<<8; + len /= 2; + for(t=0;t>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 */ @@ -1614,7 +1888,7 @@ ttf_t* ttf_load(void*data, int length) readU16(&r); //range shift if(num_tables*16 > length) { - msg(" Truncated TTC file (table entries: %d)", num_tables); + msg(" Truncated TTF file (table entries: %d)", num_tables); if(ttf->version != OPENTYPE && ttf->version != TRUETYPE_MACOS && ttf->version != VERSION_1_0) { @@ -1672,8 +1946,11 @@ void ttf_create_truetype_tables(ttf_t*ttf) ttf->hea = hea_new(ttf); if(!ttf->os2) ttf->os2 = os2_new(ttf); + if(!ttf->post) + ttf->post = post_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); @@ -1734,6 +2011,8 @@ ttf_table_t* ttf_write(ttf_t*ttf) writeBlock(file, zero, (-t->len)&3); //pad } U32 checksum = 0xb1b0afba - ttf_table_checksum(file); + if(checksum_adjust) + *checksum_adjust = checksum; U8*checksum2 = file->data + head_pos + 8; checksum2[0] = checksum>>24; checksum2[1] = checksum>>16; @@ -1741,9 +2020,125 @@ ttf_table_t* ttf_write(ttf_t*ttf) checksum2[3] = checksum>>0; return file; } + +ttf_table_t* ttf_eot_head(ttf_t*ttf) +{ + ttf_table_t*file = ttf_table_new(0); + writeU32(file, 0); //file size (filled in later) + writeU32(file, 0); //fontdatasize (filled in later) + writeU32(file, 0x01000200); + writeU32(file, 0); //flags + writeU8(file, ttf->os2->panose_FamilyType); + writeU8(file, ttf->os2->panose_SerifStyle); + writeU8(file, ttf->os2->panose_Weight); + writeU8(file, ttf->os2->panose_Proportion); + writeU8(file, ttf->os2->panose_Contrast); + writeU8(file, ttf->os2->panose_StrokeVariation); + writeU8(file, ttf->os2->panose_ArmStyle); + writeU8(file, ttf->os2->panose_Letterform); + writeU8(file, ttf->os2->panose_Midline); + writeU8(file, ttf->os2->panose_XHeight); + writeU8(file, 1); //charset (default) + writeU8(file, ttf->os2->fsSelection&1); //italic + writeU32_LE(file, ttf->os2->usWeightClass); + writeU16(file, 0); //fstype + writeU16(file, 0x4c50); //magic + writeU32_LE(file, ttf->os2->ulCharRange[0]); + writeU32_LE(file, ttf->os2->ulCharRange[1]); + writeU32_LE(file, ttf->os2->ulCharRange[2]); + writeU32_LE(file, ttf->os2->ulCharRange[3]); + writeU32_LE(file, ttf->os2->ulCodePageRange1); + writeU32_LE(file, ttf->os2->ulCodePageRange2); + writeU32(file, 0); //checksum adjust (filled in later) + writeU32(file, 0); //reserved[0] + writeU32(file, 0); //reserved[1] + writeU32(file, 0); //reserved[2] + writeU32(file, 0); //reserved[3] + writeU16(file, 0); //padding(1) + + int t,len; + + //family name + len = strlen(ttf->family_name); + writeU16_LE(file, len*2); + for(t=0;tfamily_name[t]); + } + writeU16(file, 0); //zero byte pad + + //subfamily name + len = strlen(ttf->subfamily_name); + writeU16_LE(file, len*2); + for(t=0;tsubfamily_name[t]); + } + writeU16(file, 0); //zero byte pad + + //version string + len = strlen(ttf->version_string); + writeU16_LE(file, len*2); //len + for(t=0;tversion_string[t]); + } + writeU16(file, 0); //zero byte pad + + //full name + len = strlen(ttf->full_name); + writeU16_LE(file, len*2); //len + for(t=0;tfull_name[t]); + } + writeU16(file, 0); //zero byte pad + + writeU16(file, 0); //padding(2) + return file; +} + +void ttf_save_eot(ttf_t*ttf, const char*filename) +{ + ttf_table_t* eot = ttf_eot_head(ttf); + U32 checksum_adjust = 0; + ttf_table_t* t = ttf_write(ttf, &checksum_adjust); + + U8*len_data = eot->data; + U32 full_len = eot->len + t->len; + len_data[0] = full_len>>0; + len_data[1] = full_len>>8; + len_data[2] = full_len>>16; + len_data[3] = full_len>>24; + + U8*len_data2 = eot->data+4; + len_data2[0] = t->len>>0; + len_data2[1] = t->len>>8; + len_data2[2] = t->len>>16; + len_data2[3] = t->len>>24; + + U8*checksum_data = eot->data + 60; + checksum_data[0] = checksum_adjust>>0; + checksum_data[1] = checksum_adjust>>8; + checksum_data[2] = checksum_adjust>>16; + checksum_data[3] = checksum_adjust>>24; + + FILE*fi = fopen(filename, "wb"); + if(!fi) { + perror(filename); + return; + } + + fwrite(eot->data, eot->len, 1, fi); + fwrite(t->data, t->len, 1, fi); + fclose(fi); + ttf_table_delete(0, t); + ttf_table_delete(0, eot); +} + void ttf_save(ttf_t*ttf, const char*filename) { - ttf_table_t* t = ttf_write(ttf); + ttf_table_t* t = ttf_write(ttf, 0); FILE*fi = fopen(filename, "wb"); if(!fi) { perror(filename); @@ -1753,6 +2148,7 @@ void ttf_save(ttf_t*ttf, const char*filename) 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)":""); @@ -1772,7 +2168,7 @@ void ttf_dump(ttf_t*ttf) maxp_dump(ttf); glyf_dump(ttf); } -void ttf_destroy(ttf_t*ttf) +void ttf_destroy_tables(ttf_t*ttf) { ttf_table_t*table = ttf->tables; while(table) { @@ -1781,11 +2177,22 @@ 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); + name_delete(ttf); free(ttf); } @@ -1799,12 +2206,24 @@ int main(int argn, const char*argv[]) //msg(" Loading %s", filename); memfile_t*m = memfile_open(filename); ttf_t*ttf = ttf_load(m->data, m->len); + if(!ttf) { + msg(" Couldn't load %s", filename); + return 1; + } + ttf_reduce(ttf); + + ttf->full_name = strdup("Test-Normal"); + ttf->family_name = strdup("Test"); + ttf->subfamily_name = strdup("Normal"); + ttf->version_string = strdup("Version 1.0"); + 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, "output.ttf"); + ttf_save_eot(ttf, "testfont.eot"); + ttf_save(ttf, "testfont.ttf"); ttf_destroy(ttf); return 0;