mirror of
https://github.com/rsms/inter.git
synced 2024-11-27 09:49:07 +03:00
320 lines
5.7 KiB
C
320 lines
5.7 KiB
C
|
/*
|
||
|
* Copyright (C) 2013 Jan Bobrowski <jb@wizard.ae.krakow.pl>
|
||
|
*
|
||
|
* 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 <stdlib.h>
|
||
|
#include <stdio.h>
|
||
|
#include <assert.h>
|
||
|
#include "ttf2woff.h"
|
||
|
|
||
|
struct table *find_table(struct ttf *ttf, char tag[4])
|
||
|
{
|
||
|
u32 tg = g32((u8*)tag);
|
||
|
int i;
|
||
|
for(i=0; i<ttf->ntables; 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;i<n;i++) {
|
||
|
u32 o = g32(loca->buf.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.len<b.len ? a.len : b.len;
|
||
|
while(o) {
|
||
|
if(memcmp(a.len-o+a.ptr, b.ptr, o)==0)
|
||
|
break;
|
||
|
o--;
|
||
|
}
|
||
|
return o;
|
||
|
}
|
||
|
|
||
|
static u8 *bufbuf(struct buf a, struct buf b)
|
||
|
{
|
||
|
u8 *p=a.ptr, *e=a.ptr+a.len-b.len;
|
||
|
while(p<=e) {
|
||
|
if(memcmp(p,b.ptr,b.len)==0)
|
||
|
return p;
|
||
|
p++;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int name_cmp_len(const void *va, const void *vb) {
|
||
|
struct buf a = *(struct buf*)va;
|
||
|
struct buf b = *(struct buf*)vb;
|
||
|
int d = a.len - b.len;
|
||
|
if(!d) d = memcmp(a.ptr, b.ptr, a.len);
|
||
|
return d;
|
||
|
}
|
||
|
|
||
|
static void optimize_name(struct ttf *ttf)
|
||
|
{
|
||
|
struct table *name = find_table(ttf, "name");
|
||
|
struct buf str, new;
|
||
|
struct buf *ent;
|
||
|
u8 *p;
|
||
|
int count,n,i;
|
||
|
|
||
|
if(!name || name->buf.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<n; i++) {
|
||
|
unsigned l = g16(p+8);
|
||
|
unsigned o = g16(p+10);
|
||
|
if(o+l > 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<n;j++) for(i=1;i<n;i++) if(i!=j) {
|
||
|
int o;
|
||
|
a = ent[i];
|
||
|
b = ent[j];
|
||
|
if(bufbuf(a,b))
|
||
|
goto remove_b;
|
||
|
o = overlap(a,b);
|
||
|
if(o > 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 || a.ptr>=str.ptr+str.len)
|
||
|
my_free(a.ptr);
|
||
|
|
||
|
i = mi<mj ? mi : mj;
|
||
|
j = mi<mj ? mj : mi;
|
||
|
ent[i] = c;
|
||
|
|
||
|
remove_b:
|
||
|
if(b.ptr<str.ptr || b.ptr>=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<n;i++)
|
||
|
sz += ent[i].len;
|
||
|
|
||
|
if(sz >= 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<n;i++) {
|
||
|
struct buf a = ent[i];
|
||
|
memcpy(p,a.ptr,a.len); p+=a.len;
|
||
|
if(a.ptr<str.ptr || a.ptr>=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;i<count;i++) {
|
||
|
struct buf a = {str.ptr+g16(p), g16(p-2)};
|
||
|
u8 *s = bufbuf(newstr, a);
|
||
|
assert(s);
|
||
|
p16(p, s-newstr.ptr);
|
||
|
p += 12;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#ifndef NDEBUG
|
||
|
for(i=0; i<count; i++) {
|
||
|
u8 *p0 = name->buf.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; i<nlhm; i++) {
|
||
|
p = p16(p, g16(q+2));
|
||
|
q += 4;
|
||
|
}
|
||
|
p = append(p, q, buf.ptr+buf.len-q);
|
||
|
assert(p == new.ptr+new.len);
|
||
|
|
||
|
optimized(hmtx, new);
|
||
|
|
||
|
p16(hhea->buf.ptr+34, n);
|
||
|
hhea->modified = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void optimize(struct ttf *ttf)
|
||
|
{
|
||
|
optimize_loca(ttf);
|
||
|
optimize_name(ttf);
|
||
|
optimize_hmtx(ttf);
|
||
|
}
|