+static void grow_unicode(ttf_t*ttf, int index)
+{
+ int size = index+1;
+ if(!ttf->unicode) {
+ ttf->unicode = rfx_calloc(sizeof(ttf->unicode[0])*size);
+ } else if(ttf->unicode_size<size) {
+ ttf->unicode = rfx_realloc(ttf->unicode, sizeof(ttf->unicode[0])*size);
+ memset(ttf->unicode+ttf->unicode_size, 0, sizeof(ttf->unicode[0])*(size - ttf->unicode_size));
+ }
+ 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("<warning> CMap overflow");
+ num_subtables = (r->size-r->pos)/8;
+ }
+ unicode_t*data = 0;
+ for(t=0;t<num_subtables;t++) {
+ U16 platform = readU16(r);
+ U16 encoding = readU16(r);
+ U32 offset = readU32(r);
+ if(offset>r->size) {
+ msg("<warning> CMAP table %d %d is out of bounds (%d)", platform, encoding, offset);
+ continue;
+ }
+
+ int is_unicode = platform==0 ||
+ platform==3 && encoding == 1 ||
+ platform==3 && encoding == 10;
+
+ if(!is_unicode)
+ continue;
+
+ INIT_READ(t, r->mem, r->size, offset);
+ U16 format = readU16(&t);
+ int length = readU16(&t);
+ U16 language = readU16(&t);
+
+ if(language)
+ msg("<warning> Language code %02x in unicode mapping", language);
+
+ int num = 0;
+ if(format == 0) {
+ num = length-6;
+ if(t.pos+length > t.size) {
+ msg("<warning> overflow in format 0 cmap table");
+ num = t.size-t.pos;
+ }
+ data = malloc(num*sizeof(unicode_t));
+ int s;
+ grow_unicode(ttf, num);
+ for(s=0;s<num;s++) {
+ ttf->unicode[s] = readU8(&t);
+ }
+ } else if(format == 4) {
+ U16 segment_count = readU16(&t);
+ if(segment_count&1) {
+ msg("<error> Bad segmentx2 count %d", segment_count);
+ continue;
+ }
+ segment_count>>=1;
+ readU16(&t); //searchrange
+ readU16(&t); //entry selector
+ readU16(&t); //range shift
+ INIT_READ(r_end, t.mem, t.size, t.pos);
+ INIT_READ(r_start, t.mem, t.size, t.pos+2+segment_count*2);
+ 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);
+ U16 end = readU16(&r_end);
+ U16 delta = readU16(&r_delta);
+ U16 range = readU16(&r_range);
+ if(start==0xffff && end==0xffff && delta==1) {
+ /* this is a common (maybe even required) occurence in fonts
+ which explicitly map "unicode undefined" (0xffff) to
+ "glyph undefined" (0).
+ We don't want to blow our unicode table up to 65536 just
+ because of this, so ignore this entry.
+ */
+ continue;
+ }
+ grow_unicode(ttf, end);
+ int u;
+ if(!range) {
+ for(u=start;u<=end;u++) {
+ ttf->unicode[u] = (u + delta) & 0xffff;
+ }
+ } else {
+ 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);
+ }
+ }
+ }
+ }
+ }
+}
+static int segment_size(unicode_t*unicode, int pos, int size)
+{
+ int s;
+ int count=0;
+ for(s=pos;s<size;s++) {
+ if(!unicode[s])
+ count++;
+ if(count>4) {
+ /* 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;
+ }
+ }
+ s -= count; // go to the last filled in entry
+ if(s==size)
+ return size-1;
+ return s;
+}
+void cmap_write(ttf_t* ttf, ttf_table_t*w)
+{
+ writeU16(w, 0); //version
+ writeU16(w, 2); //two tables
+
+ writeU16(w, 0); //platform (unicode)
+ writeU16(w, 3); //encoding (unicode 2.0)
+ writeU32(w, 20); //offset
+
+ writeU16(w, 3); //platform (windows)
+ writeU16(w, 1); //encoding (unicode basic multilingual plane UCS-2)
+ writeU32(w, 20); //offset
+
+ writeU16(w, 4); // format=4
+ int length_pos = w->len;
+ writeU16(w, 0); // length: we don't know yet
+ writeU16(w, 0); // language (n/a for unicode)
+ int num_segments_pos = w->len;
+ writeU16(w, 0); //number of segments: we don't know yet either
+ writeU16(w, 0); //searchrange
+ writeU16(w, 0); //entry selector
+ writeU16(w, 0); //range shift
+
+ int pos=0;
+ int num_segments=0;
+ while(pos < ttf->unicode_size) {
+ if(!ttf->unicode[pos]) {
+ pos++;
+ continue;
+ }
+ int s = segment_size(ttf->unicode, pos, ttf->unicode_size);
+ 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;t<num_segments;t++) {writeU16(w, 0);} //end array
+ writeU16(w, 0); //reserved byte
+ int start_pos = w->len;
+ for(t=0;t<num_segments;t++) {writeU16(w, 0);} //start array
+ int delta_pos = w->len;
+ 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
+
+ /* 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;
+ while(pos < ttf->unicode_size) {
+ if(!ttf->unicode[pos]) {
+ pos++;
+ continue;
+ }
+ U16 end = segment_size(ttf->unicode, pos, ttf->unicode_size);
+ w->data[end_pos++]=end>>8;
+ w->data[end_pos++]=end;
+ w->data[start_pos++]=pos>>8;
+ w->data[start_pos++]=pos;
+ int s;
+ U16 delta = ttf->unicode[pos]-pos;
+ char do_delta=1;
+ for(s=pos+1;s<=end;s++) {
+ U16 delta2 = ttf->unicode[s]-s;
+ if(delta2!=delta) {
+ do_delta=0;
+ break;
+ }
+ }
+ U16 range;
+ if(do_delta) {
+ range = 0;
+ } else {
+ delta = 0;
+ range = w->len - range_pos;
+ for(s=pos;s<=end;s++) {
+ writeU16(w, ttf->unicode[s]);
+ }
+ }
+ w->data[delta_pos++]=delta>>8;
+ w->data[delta_pos++]=delta;
+ w->data[range_pos++]=range>>8;
+ w->data[range_pos++]=range;
+ 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;
+}
+void cmap_delete(ttf_t*ttf)
+{
+ if(ttf->unicode) {
+ free(ttf->unicode);
+ ttf->unicode=0;
+ }
+ 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;t<count;t++) {
+ U16 platform = readU16(r);
+ U16 encoding = readU16(r);
+ U16 language = readU16(r);
+ U16 name_id = readU16(r);
+ U16 len = readU16(r);
+ U16 offset_2 = readU16(r);
+
+ char ** read_name = 0;
+
+ 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);
+
+ 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;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)
+
+ /* Windows expects the name table to be sorted by platform/encoding/language/name_id */
+ 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;
+ }
+ }
+ for(t=0;t<nr;t++) {
+ if(strings[t]) {
+ writeU16(table, 3); //platform id (windows)
+ writeU16(table, 1); //encoding id (ucs-2)
+ writeU16(table, 0x409); //language (US)
+ writeU16(table, codes[t]);
+ int len2 = strlen(strings[t]) * 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);
+ }
+ }
+ for(t=0;t<nr;t++) {
+ if(strings[t]) {
+ int s;
+ int len = strlen(strings[t]);
+ for(s=0;s<len;s++) {
+ writeU8(table, 0);
+ writeU8(table, strings[t][s]);
+ }
+ }
+ }
+}
+void name_delete(ttf_t*ttf)
+{
+ 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;
+ }
+ 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;t<cvt->num;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;t<cvt->num;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;t<num;t++) {
+ gasp->records[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;t<gasp->num;t++) {
+ if(gasp->records[t].behaviour & ~(GASP_GRIDFIT | GASP_DOGRAY)) {
+ version = 1;
+ }
+ }
+ writeU16(table, version);
+ writeU16(table, gasp->num);
+ for(t=0;t<gasp->num;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;
+ }
+}
+