/* * Copyright (C) 2013 Jan Bobrowski * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. */ #include #include #include #include "ttf2woff.h" struct table *find_table(struct ttf *ttf, char tag[4]) { u32 tg = g32((u8*)tag); int i; for(i=0; intables; i++) if(ttf->tables[i].tag == tg) return &ttf->tables[i]; return 0; } static void replace_table(struct table *t, u8 *p, int l) { if(t->free_buf) t->buf.ptr = my_free(t->buf.ptr); t->free_buf = 1; t->modified = 1; t->buf.ptr = p; t->buf.len = l; } static void optimized(struct table *t, struct buf new) { if(g.verbose) echo("Optimized %s table: %u > %u (%d bytes)", t->name, t->buf.len, new.len, new.len-t->buf.len); replace_table(t, new.ptr, new.len); } static void optimize_loca(struct ttf *ttf) { struct table *head, *loca, *glyf; struct buf new; int i,n; head = find_table(ttf, "head"); loca = find_table(ttf, "loca"); glyf = find_table(ttf, "glyf"); if(!head || !loca || !glyf) return; if(head->buf.len<54 || g16(head->buf.ptr+50)!=1) return; if(loca->buf.len&3 || loca->buf.len<4) return; // we have 32-bit loca table if(glyf->buf.len != g32(loca->buf.ptr+loca->buf.len-4)) return; if(glyf->buf.len >= 1<<16) return; n = loca->buf.len>>2; new.len = 2*n; new.ptr = my_alloc(new.len); for(i=0;ibuf.ptr+4*i); if(o&1) { echo("Bad offset in loca"); my_free(new.ptr); return; } p16(new.ptr+2*i, o>>1); } optimized(loca, new); p16(head->buf.ptr+50, 0); head->modified = 1; } static int overlap(struct buf a, struct buf b) { int o = a.lenbuf.len<6+2*12+1 || g16(name->buf.ptr)) return; n = g16(name->buf.ptr+4); // stringOffset if(name->buf.len < n) goto corrupted; str.ptr = name->buf.ptr+n; str.len = name->buf.len-n; count = g16(name->buf.ptr+2); if(name->buf.len < 6+12*count) { corrupted: echo("Name table corrupted"); return; } n = count; ent = my_alloc(n * sizeof *ent); p = name->buf.ptr+6; for(i=0; i str.len) { echo("Bad string location in name table"); my_free(ent); return; } if(l) { ent[i].ptr = str.ptr + o; ent[i].len = l; } p += 12; } qsort(ent, n, sizeof *ent, name_cmp_len); for(;;) { int j,mo,mi,mj; struct buf a, b, c; mo = 0; for(j=0;j mo) { mo = o; mi = i; mj = j; } } if(!mo) break; a = ent[mi]; b = ent[mj]; c.len = a.len + b.len - mo; c.ptr = my_alloc(c.len); p = append(c.ptr, a.ptr, a.len); append(p, b.ptr+mo, b.len-mo); if(a.ptr=str.ptr+str.len) my_free(a.ptr); i = mi=str.ptr+str.len) my_free(b.ptr); n--; while(j < n) ent[j]=ent[j+1], j++; } { int sz = 6 + 12*count; for(i=0;i= name->buf.len) { my_free(ent); return; } new.len = sz; new.ptr = my_alloc(sz); p = new.ptr + 6 + 12*count; for(i=0;i=str.ptr+str.len) my_free(a.ptr); } assert(p == new.ptr+new.len); } my_free(ent); memcpy(new.ptr, name->buf.ptr, 6+12*count); p16(new.ptr+4,6+12*count); { struct buf newstr; newstr.ptr = new.ptr + 6+12*count; newstr.len = new.len - 6+12*count; p = new.ptr + 6 + 10; for(i=0;ibuf.ptr; u8 *p1 = new.ptr; p0 += g16(p0+4) + g16(p0+6+12*i+10); p1 += g16(p1+4) + g16(p1+6+12*i+10); assert(!memcmp(p0,p1,g16(new.ptr+6+12*i+8))); } #endif optimized(name, new); } static void optimize_hmtx(struct ttf *ttf) { struct table *hhea, *hmtx; struct buf buf; u8 *p, *q; int nlhm,adv,n; hhea = find_table(ttf, "hhea"); hmtx = find_table(ttf, "hmtx"); if(!hhea || !hmtx || hhea->buf.len < 36 || g32(hhea->buf.ptr)!=0x10000) return; nlhm = g16(hhea->buf.ptr + 34); buf = hmtx->buf; if(!nlhm || buf.len&1 || buf.len < 4*nlhm) { return; } if(nlhm<2) return; p = buf.ptr + 4*(nlhm-1); adv = g16(p); for(n=nlhm; n>1; n--) { p -= 4; if(adv != g16(p)) break; } if(n < nlhm) { struct buf new; int i, nent = (buf.len>>1) - nlhm; new.len = 2*nent + 2*n; new.ptr = my_alloc(new.len); p = append(new.ptr, buf.ptr, n<<2); q = buf.ptr + (n<<2); for(i=n; ibuf.ptr+34, n); hhea->modified = 1; } } void optimize(struct ttf *ttf) { optimize_loca(ttf); optimize_name(ttf); optimize_hmtx(ttf); }