#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
+#include <assert.h>
#include "log.h"
#include "os.h"
+#include "q.h"
#include "mem.h"
#include "ttf.h"
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)
w->data[w->len++] = v>>8;
w->data[w->len++] = v;
}
+static inline void writeU16_LE(ttf_table_t*w, unsigned short v)
+{
+ if(w->memsize<w->len+2)
+ expand(w, w->len+2);
+ w->data[w->len++] = v;
+ w->data[w->len++] = v>>8;
+}
#define writeS16 writeU16
static inline void writeU32(ttf_table_t*w, unsigned long v)
{
w->data[w->len++] = v>>8;
w->data[w->len++] = v;
}
+static inline void writeU32_LE(ttf_table_t*w, unsigned long v)
+{
+ if(w->memsize<w->len+4)
+ expand(w, w->len+4);
+ w->data[w->len++] = v;
+ w->data[w->len++] = v>>8;
+ w->data[w->len++] = v>>16;
+ w->data[w->len++] = v>>24;
+}
static inline void writeBlock(ttf_table_t*w, void*data, int len)
{
if(w->memsize<w->len+len)
{
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;t<ttf->num_glyphs;t++) {
if(ttf->glyphs[t].num_points>max)
max = ttf->glyphs[t].num_points;
readU16(r); // version (0)
int num_subtables = readU16(r);
int t;
+ char warn=1;
if(r->pos+num_subtables*8 > r->size) {
msg("<warning> CMap overflow");
num_subtables = (r->size-r->pos)/8;
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;s<segment_count;s++) {
U16 start = readU16(&r_start);
ttf->unicode[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("<warning> 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);
}
num_segments++; // account for 0xffff mapping
+ int glyphmap_start = w->len+2+num_segments*8;
+
int t;
int end_pos = w->len;
for(t=0;t<num_segments;t++) {writeU16(w, 0);} //end array
for(t=0;t<num_segments;t++) {writeU16(w, 0);} //delta array
int range_pos = w->len;
for(t=0;t<num_segments;t++) {writeU16(w, 0);} //range array
-
+
w->data[num_segments_pos]=(num_segments*2)>>8;
w->data[num_segments_pos+1]=(num_segments*2);
-
+
pos=0;
num_segments = 0;
while(pos < ttf->unicode_size) {
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;
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]);
}
}
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 name_id = readU16(r);
U16 len = readU16(r);
U16 offset_2 = readU16(r);
- /*printf("%d %d %d %d at %d, %d bytes:", platform, encoding, language, name_id, offset+offset_2, len);
- int s;
- for(s=0;s<len;s++) {
- printf("%c", r->mem[offset+offset_2+s]);
+
+ INIT_READ(ss, r->mem, r->size, offset+offset_2);
+ if(!(platform==0 || (platform==1 && encoding==0)))
+ continue;
+
+ INIT_READ(s, r->mem, r->size, offset+offset_2);
+
+ 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);
}
- printf("\n");*/
}
}
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
- writeU16(table, 1); //count
- int offset = 18;
- writeU16(table, offset); //offset
-
- writeU16(table, 1); //platform id
- writeU16(table, 0); //encoding id
- writeU16(table, 0); //language
- writeU16(table, 4); //4: full name
- int len = strlen(ttf->name);
- writeU16(table, len);
- writeU16(table, table->len+2 - offset);
+ int count = 0;
int t;
- for(t=0;t<len;t++) {
- writeU8(table, ttf->name[t]);
+ int nr = sizeof(strings)/sizeof(strings[0]);
+
+ for(t=0;t<nr;t++) {
+ if(strings[t])
+ count+=2;
+ }
+ writeU16(table, count); //count
+
+ int offset_pos = table->len;
+ writeU16(table, 0); //offset (will be filled in later)
+
+ int offset = 0;
+ for(t=0;t<nr;t++) {
+ if(strings[t]) {
+ writeU16(table, 1); //platform id (mac)
+ writeU16(table, 0); //encoding id (latin-1)
+ writeU16(table, 0); //language (english)
+ writeU16(table, codes[t]);
+ int len = strlen(strings[t]);
+ writeU16(table, len);
+ writeU16(table, offset);
+ offset += len;
+
+ writeU16(table, 3); //platform id (windows)
+ writeU16(table, 1); //encoding id (ucs-2)
+ writeU16(table, 0x409); //language (US)
+ writeU16(table, codes[t]);
+ int len2 = len * 2;
+ writeU16(table, len2);
+ writeU16(table, offset);
+ offset += len2;
+ }
+ }
+ table->data[offset_pos] = table->len>>8;
+ table->data[offset_pos+1] = table->len;
+
+ for(t=0;t<nr;t++) {
+ if(strings[t]) {
+ int len = strlen(strings[t]);
+ writeBlock(table, strings[t], len);
+ int s;
+ for(s=0;s<len;s++) {
+ writeU8(table, 0);
+ writeU8(table, strings[t][s]);
+ }
+ }
}
}
void name_delete(ttf_t*ttf)
{
- if(ttf->name)
- free(ttf->name);
+ if(ttf->full_name) {
+ free(ttf->full_name);
+ ttf->full_name=0;
+ }
+ if(ttf->family_name) {
+ free(ttf->family_name);
+ ttf->family_name=0;
+ }
+ if(ttf->subfamily_name) {
+ free(ttf->subfamily_name);
+ ttf->subfamily_name=0;
+ }
+ if(ttf->version_string) {
+ free(ttf->version_string);
+ ttf->version_string=0;
+ }
+}
+
+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)
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->name) {
+ 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);
head_write(ttf, table, loca_size);
ttf_t*ttf = rfx_calloc(sizeof(ttf_t));
ttf->version = readU32(&r);
+ if(ttf->version == SWAP32(length)) {
+ U32 fontDataSize = readU32(&r);
+ U32 version = readU32(&r);
+ U32 flags = readU32(&r);
+ U8 panose[10];
+ readBlock(&r, panose, 10);
+ readU8(&r); //charset
+ readU8(&r); //italoc
+ readU32(&r); //weight
+ readU16(&r); //fstype
+ U16 magic = readU16(&r); //magicNumber
+ /* we're being paranoid: it's entirely possible for the font
+ size to be exactly 0x10000. Only treat this font as eot if
+ it has the right magic number */
+ if(magic == 0x4c50) {
+ readU32(&r); //unicoderange[0]
+ readU32(&r); //unicoderange[1]
+ readU32(&r); //unicoderange[2]
+ readU32(&r); //unicoderange[3]
+ readU32(&r); //codepagerange[0]
+ readU32(&r); //codepagerange[1]
+ readU32(&r); //checksumadjustment
+ readU32(&r); //reserved[0]
+ readU32(&r); //reserved[1]
+ readU32(&r); //reserved[2]
+ readU32(&r); //reserved[3]
+ readU16(&r); //padding
+
+ int nr=0;
+ for(nr=0;nr<4;nr++) {
+ int t, len;
+ /* All of ttf is big-endian. All of ttf? No. One small eot table
+ of indomitable little-endian... */
+ len = readU8(&r);
+ len |= readU8(&r)<<8;
+ len /= 2;
+ for(t=0;t<len;t++) {
+ U8 c = readU16(&r)>>8;
+ }
+ readU16(&r); // zero terminator
+ }
+ readU16(&r); // more padding
+
+ /* adjust the offset to the start of the actual truetype
+ data- the positions in the table header will be relative
+ to the ttf data after the header, not to the file */
+ r.mem += r.pos;
+ r.size -= r.pos;
+ r.pos = 0;
+ ttf->version = readU32(&r);
+ } else {
+ reader_reset(&r);
+ ttf->version = readU32(&r);
+ }
+ }
+
if(ttf->version == TTCFTAG) {
/* a ttc collection is a number of truetype fonts
packaged together */
readU16(&r); //range shift
if(num_tables*16 > length) {
- msg("<error> Truncated TTC file (table entries: %d)", num_tables);
+ msg("<error> Truncated TTF file (table entries: %d)", num_tables);
if(ttf->version != OPENTYPE &&
ttf->version != TRUETYPE_MACOS &&
ttf->version != VERSION_1_0) {
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);
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;
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;t<len;t++) {
+ writeU8(file, 0);
+ writeU8(file, ttf->family_name[t]);
+ }
+ writeU16(file, 0); //zero byte pad
+
+ //subfamily name
+ len = strlen(ttf->subfamily_name);
+ writeU16_LE(file, len*2);
+ for(t=0;t<len;t++) {
+ writeU8(file, 0);
+ writeU8(file, ttf->subfamily_name[t]);
+ }
+ writeU16(file, 0); //zero byte pad
+
+ //version string
+ len = strlen(ttf->version_string);
+ writeU16_LE(file, len*2); //len
+ for(t=0;t<len;t++) {
+ writeU8(file, 0);
+ writeU8(file, ttf->version_string[t]);
+ }
+ writeU16(file, 0); //zero byte pad
+
+ //full name
+ len = strlen(ttf->full_name);
+ writeU16_LE(file, len*2); //len
+ for(t=0;t<len;t++) {
+ writeU8(file, 0);
+ writeU8(file, ttf->full_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);
fclose(fi);
ttf_table_delete(0, t);
}
+
void ttf_dump(ttf_t*ttf)
{
msg("<notice> Truetype file version %08x%s", ttf->version, ttf->version == OPENTYPE?" (opentype)":"");
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) {
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);
}
//msg("<notice> Loading %s", filename);
memfile_t*m = memfile_open(filename);
ttf_t*ttf = ttf_load(m->data, m->len);
- ttf->name = strdup("testfont");
+ if(!ttf) {
+ msg("<error> 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;