#include #include #include #include #include "impl.h" typedef struct Hint Hint; enum { debug = 0 }; #pragma varargck type "π" TTPoint #define dprint(...) {if(debug) fprint(2, __VA_ARGS__);} static TTGState defstate = { .fvx = 16384, .fvy = 0, .pvx = 16384, .pvy = 0, .dpvx = 16384, .dpvy = 0, .instctrl = 0, .scanctrl = 0, .rperiod = 64, .rphase = 0, .rthold = 32, .zp = 7, .cvci = 68, .loop = 1, .singlewval = 0, .singlewci = 0, .deltabase = 9, .deltashift = 3, .autoflip = 1, .mindist = 64, }; struct Hint { TTFont *f; TTGlyph *g; u8int *shint, *ip, *ehint; u32int *stack; int sp, nstack; int level; char err[ERRMAX]; jmp_buf jmp; }; int rounddiv(int a, int b) { if(b < 0){ a = -a; b = -b; } if(a > 0) return (a + b/2) / b; else return (a - b/2) / b; } int vrounddiv(vlong a, int b) { if(b < 0){ a = -a; b = -b; } if(a > 0) return (a + b/2) / b; else return (a - b/2) / b; } static void herror(Hint *h, char *fmt, ...) { va_list va; va_start(va, fmt); vsnprint(h->err, sizeof(h->err), fmt, va); va_end(va); dprint("error: %s\n", h->err); longjmp(h->jmp, 1); } static void push(Hint *h, u32int w) { assert(h->sp < h->nstack); h->stack[h->sp++] = w; } static u32int pop(Hint *h) { assert(h->sp > 0); return h->stack[--h->sp]; } static u8int fetch8(Hint *h) { if(h->ip == h->ehint) herror(h, "missing byte"); return *h->ip++; } enum { RP0 = 0x10, RP1 = 0x20, RP2 = 0x30, ZP0 = 0, ZP1 = 0x100, ZP2 = 0x200, ORIG = 0x1000, NOTOUCH = 0x2000, }; static TTPoint getpoint(Hint *h, int n, int pi) { if((n & RP2) != 0) pi = h->f->rp[(n >> 4 & 3) - 1]; if((h->f->zp >> (n >> 8 & 3) & 1) != 0){ if(h->g == nil) herror(h, "access to glyph zone from FPGM/CVT"); if((uint)pi >= h->g->npt) herror(h, "glyph zone point index %d out of range", pi); dprint("G%s%d: %+π\n", n&ORIG?"O":"", pi, (n & ORIG) != 0 ? h->g->ptorg[pi] : h->g->pt[pi]); return (n & ORIG) != 0 ? h->g->ptorg[pi] : h->g->pt[pi]; }else{ if((uint)pi >= h->f->u->maxTwilightPoints) herror(h, "twilight zone point index %d out of range", pi); return (n & ORIG) != 0 ? h->f->twiorg[pi] : h->f->twilight[pi]; } } static void setpoint(Hint *h, int n, int pi, TTPoint p) { if((n & RP2) != 0) pi = h->f->rp[(n >> 4 & 3) - 1]; if((n & NOTOUCH) == 0){ if(h->f->fvx != 0) p.flags |= 2; if(h->f->fvy != 0) p.flags |= 4; } if((h->f->zp >> (n >> 8 & 3) & 1) != 0){ if(h->g == nil) herror(h, "access to glyph zone from FPGM/CVT"); if((uint)pi >= h->g->npt) herror(h, "glyph zone point index %d out of range", pi); dprint("G%d: %+π -> %+π\n", pi, h->g->pt[pi], p); h->g->pt[pi] = p; }else{ if((uint)pi >= h->f->u->maxTwilightPoints) herror(h, "twilight zone point index %d out of range", pi); dprint("T%d: %+π -> %+π\n", pi, h->f->twilight[pi], p); h->f->twilight[pi] = p; } } static TTPoint getpointz(Hint *h, int z, int pi) { if((z & 1) != 0){ if(h->g == nil) herror(h, "access to glyph zone from FPGM/CVT"); if((uint)pi >= h->g->npt) herror(h, "glyph zone point index %d out of range", pi); dprint("G%s%d: %+π\n", z&ORIG?"O":"", pi, (z & ORIG) != 0 ? h->g->ptorg[pi] : h->g->pt[pi]); return (z & ORIG) != 0 ? h->g->ptorg[pi] : h->g->pt[pi]; }else{ if((uint)pi >= h->f->u->maxTwilightPoints) herror(h, "twilight zone point index %d out of range", pi); return (z & ORIG) != 0 ? h->f->twiorg[pi] : h->f->twilight[pi]; } } static void setpointz(Hint *h, int z, int pi, TTPoint p) { if((z & 1) != 0){ if(h->g == nil) herror(h, "access to glyph zone from FPGM/CVT"); if((uint)pi >= h->g->npt) herror(h, "glyph zone point index %d out of range", pi); dprint("G%d: %+π -> %+π\n", pi, h->g->pt[pi], p); h->g->pt[pi] = p; }else{ if((uint)pi >= h->f->u->maxTwilightPoints) herror(h, "twilight zone point index %d out of range", pi); dprint("T%d: %+π -> %+π\n", pi, h->f->twilight[pi], p); h->f->twilight[pi] = p; } } static void debugprint(Hint *h, int skip) { Fmt f; char buf[256]; static char *opcnames[256] = { [0x00] "SVTCA", "SVTCA", "SPVTCA", "SPVTCA", "SFVTCA", "SFVTCA", "SPVTL", "SPVTL", [0x08] "SFVTL", "SFVTL", "SPVFS", "SFVFS", "GPV", "GFV", "SFVTPV", "ISECT", [0x10] "SRP0", "SRP1", "SRP2", "SZP0", "SZP1", "SZP2", "SZPS", "SLOOP", [0x18] "RTG", "RTHG", "SMD", "ELSE", "JMPR", "SCVTCI", "SSWCI", "SSW", [0x20] "DUP", "POP", "CLEAR", "SWAP", "DEPTH", "CINDEX", "MINDEX", "ALIGNPTS", [0x28] nil, "UTP", "LOOPCALL", "CALL", "FDEF", "ENDF", "MDAP", "MDAP", [0x30] "IUP", "IUP", "SHP", "SHP", "SHC", "SHC", "SHZ", "SHZ", [0x38] "SHPIX", "IP", "MSIRP", "MSIRP", "ALIGNRP", "RTDG", "MIAP", "MIAP", [0x40] "NPUSHB", "NPUSHW", "WS", "RS", "WCVTP", "RCVT", "GC", "GC", [0x48] "SCFS", "MD", "MD", "MPPEM", "MPS", "FLIPON", "FLIPOFF", "DEBUG", [0x50] "LT", "LTEQ", "GT", "GTEQ", "EQ", "NEQ", "ODD", "EVEN", [0x58] "IF", "EIF", "AND", "OR", "NOT", "DELTAP1", "SDB", "SDS", [0x60] "ADD", "SUB", "DIV", "MUL", "ABS", "NEG", "FLOOR", "CEILING", [0x68] "ROUND", "ROUND", "ROUND", "ROUND", "NROUND", "NROUND", "NROUND", "NROUND", [0x70] "WCVTF", "DELTAP2", "DELTAP3", "DELTAC1", "DELTAC2", "DELTAC3", "SROUND", "S45ROUND", [0x78] "JROT", "JROF", "ROFF", nil, "RUTG", "RDTG", "SANGW", "AA", [0x80] "FLIPPT", "FLIPRGON", "FLIPRGOFF", [0x85] "SCANCTRL", "SDPVTL", "SDPVTL", [0x88] "GETINFO", "IDEF", "ROLL", "MAX", "MIN", "SCANTYPE", "INSTCTRL", nil, [0xB0] "PUSHB", "PUSHB", "PUSHB", "PUSHB", "PUSHB", "PUSHB", "PUSHB", "PUSHB", [0xB8] "PUSHW", "PUSHW", "PUSHW", "PUSHW", "PUSHW", "PUSHW", "PUSHW", "PUSHW", }; static u8int argb[256] = { [0x00] 1, 1, 1, 1, 1, 1, 1, 1, [0x08] 1, 1, 1, 1, 1, 1, [0x2e] 1, 1, [0x30] 1, 1, 1, 1, 1, 1, 1, 1, [0x38] 0, 0, 1, 1, 0, 0, 1, 1, [0x46] 1, 1, 0, 1, 1, [0x68] 2, 2, 2, 2, 2, 2, 2, 2, }; u8int op; int i; fmtfdinit(&f, 2, buf, sizeof(buf)); op = *h->ip; if(skip) fmtprint(&f, "** "); fmtprint(&f, "%d %d ", h->level, (int)(h->ip - h->shint)); if(op >= 0xc0) fmtprint(&f, "%s[%d]", op >= 0xe0 ? "MIRP" : "MDRP", op & 0x1f); else if(opcnames[op] == nil) fmtprint(&f, "???"); else fmtprint(&f, argb[op] != 0 ? "%s[%d]" : "%s[]", opcnames[op], op & (1<sp; i++) fmtprint(&f, "%d ", h->stack[h->sp - 1 - i]); } fmtprint(&f, "\n"); fmtfdflush(&f); } static void h_npushb(Hint *h) { u8int n, b; n = fetch8(h); while(n-- > 0){ b = fetch8(h); push(h, b); } } static void h_npushw(Hint *h) { u8int n; u32int x; n = fetch8(h); while(n-- > 0){ x = fetch8(h) << 8; x |= fetch8(h); push(h, (short)x); } } static void h_pushb(Hint *h) { int n; u8int b; n = (h->ip[-1] & 7) + 1; while(n-- > 0){ b = fetch8(h); push(h, b); } } static void h_pushw(Hint *h) { int n; u16int w; n = (h->ip[-1] & 7) + 1; while(n-- > 0){ w = fetch8(h) << 8; w |= fetch8(h); push(h, (short)w); } } static void skip(Hint *h, int mode) { int level; level = 0; for(;;){ if(h->ip >= h->ehint) herror(h, "reached end of stream during skip()"); if(debug) debugprint(h, 1); switch(mode){ case 0: if(*h->ip == 0x2d) return; break; case 1: if(level == 0 && (*h->ip == 0x1b || *h->ip == 0x59)) return; } switch(*h->ip++){ case 0x40: case 0x41: if(h->ip < h->ehint) h->ip += *h->ip + 1; break; case 0x58: level++; break; case 0x59: level--; break; case 0xb0: case 0xb1: case 0xb2: case 0xb3: case 0xb4: case 0xb5: case 0xb6: case 0xb7: h->ip += (h->ip[-1] & 7) + 1; break; case 0xb8: case 0xb9: case 0xba: case 0xbb: case 0xbc: case 0xbd: case 0xbe: case 0xbf: h->ip += 2 * ((h->ip[-1] & 7) + 1); break; } } } static void h_fdef(Hint *h) { int i; u8int *sp; TTFont *f; f = h->f; i = pop(h); if((uint)i >= h->f->u->maxFunctionDefs) herror(h, "function identifier out of range"); sp = h->ip; skip(h, 0); f->func[i].npgm = h->ip - sp; f->func[i].pgm = mallocz(f->func[i].npgm, 1); if(f->func[i].pgm == nil) herror(h, "malloc: %r"); memcpy(f->func[i].pgm, sp, f->func[i].npgm); h->ip++; } static void run(Hint *); static void h_call(Hint *h) { int i; u8int *lip, *lshint, *lehint; i = pop(h); if((uint)i >= h->f->u->maxFunctionDefs || h->f->func[i].npgm == 0) herror(h, "undefined funcion %d", i); lip = h->ip; lshint = h->shint; lehint = h->ehint; h->ip = h->shint = h->f->func[i].pgm; h->ehint = h->ip + h->f->func[i].npgm; h->level++; run(h); h->level--; h->ip = lip; h->shint = lshint; h->ehint = lehint; } static void h_loopcall(Hint *h) { int i, n; u8int *lip, *lshint, *lehint; i = pop(h); n = pop(h); if((uint)i >= h->f->u->maxFunctionDefs || h->f->func[i].npgm == 0) herror(h, "undefined funcion %d", i); for(; n > 0; n--){ lip = h->ip; lshint = h->shint; lehint = h->ehint; h->ip = h->shint = h->f->func[i].pgm; h->ehint = h->ip + h->f->func[i].npgm; h->level++; run(h); h->level--; h->ip = lip; h->shint = lshint; h->ehint = lehint; } } static void h_dup(Hint *h) { u32int x; x = pop(h); push(h, x); push(h, x); } static void h_swap(Hint *h) { u32int x, y; x = pop(h); y = pop(h); push(h, x); push(h, y); } static void h_cindex(Hint *h) { int n; n = pop(h); if(n <= 0 || n > h->sp) herror(h, "CINDEX[%d] out of range", n); push(h, h->stack[h->sp - n]); } static void h_mindex(Hint *h) { int n, x; n = pop(h); if(n <= 0 || n > h->sp) herror(h, "MINDEX[%d] out of range", n); x = h->stack[h->sp - n]; memmove(&h->stack[h->sp - n], &h->stack[h->sp - n + 1], (n - 1) * sizeof(u32int)); h->stack[h->sp - 1] = x; } static void h_svtca(Hint *h) { int a; a = h->ip[-1]; if(a < 2 || a >= 4){ h->f->fvx = 16384 * (a & 1); h->f->fvy = 16384 * (~a & 1); } if(a < 4){ h->f->dpvx = h->f->pvx = 16384 * (a & 1); h->f->dpvy = h->f->pvy = 16384 * (~a & 1); } } static void h_instctrl(Hint *h) { int s, v; s = pop(h); v = pop(h); if(v != 0) h->f->instctrl |= 1<f->instctrl &= ~(1<f->ppem); } static int ttround(Hint *h, int x) { int y; if(h->f->rperiod == 0) return x; if(x >= 0){ y = x - h->f->rphase + h->f->rthold; y -= y % h->f->rperiod; y += h->f->rphase; if(y < 0) y = h->f->rphase; }else{ y = x + h->f->rphase - h->f->rthold; y -= y % h->f->rperiod; y -= h->f->rphase; if(y > 0) y = -h->f->rphase; } return y; } static void h_binop(Hint *h) { int a, b, r; b = pop(h); a = pop(h); switch(h->ip[-1]){ case 0x50: r = a < b; break; case 0x51: r = a <= b; break; case 0x52: r = a > b; break; case 0x53: r = a >= b; break; case 0x54: r = a == b; break; case 0x55: r = a != b; break; case 0x5a: r = a && b; break; case 0x5b: r = a || b; break; case 0x60: r = a + b; break; case 0x61: r = a - b; break; case 0x62: if(b == 0) herror(h, "division by zero"); r = (vlong)(int)a * 64 / (int)b; break; case 0x63: r = (vlong)(int)a * (vlong)(int)b >> 6; break; case 0x8b: r = a < b ? b : a; break; case 0x8c: r = a < b ? a : b; break; default: SET(r); abort(); } push(h, r); } static void h_unop(Hint *h) { u32int a, r; a = pop(h); switch(h->ip[-1]){ case 0x56: r = (ttround(h, a) / 64 & 1) != 0; break; case 0x57: r = (ttround(h, a) / 64 & 1) == 0; break; case 0x5c: r = !a; break; case 0x64: r = (int)a < 0 ? -a : a; break; case 0x65: r = -a; break; case 0x66: r = a & -64; break; case 0x67: r = -(-a & -64); break; case 0x68: case 0x69: case 0x6a: case 0x6b: r = ttround(h, a); break; default: SET(r); abort(); } push(h, r); } static void h_rs(Hint *h) { int n; n = pop(h); if((uint)n >= h->f->u->maxStorage) herror(h, "RS[%d] out of bounds"); push(h, h->f->storage[n]); } static void h_ws(Hint *h) { u32int v; int n; v = pop(h); n = pop(h); if((uint)n >= h->f->u->maxStorage) herror(h, "WS[%d] out of bounds"); h->f->storage[n] = v; } static void h_if(Hint *h) { u32int x; x = pop(h); if(!x){ skip(h, 1); h->ip++; } } static void h_else(Hint *h) { skip(h, 1); h->ip++; } static void h_nop(Hint *) { } static void h_getinfo(Hint *h) { int s; u32int r; s = pop(h); r = 0; if((s & 1) != 0) r |= 3; push(h, r); } static void h_scanctrl(Hint *h) { h->f->scanctrl = pop(h); } static void h_scantype(Hint *h) { h->f->scantype = pop(h); } static void h_roundst(Hint *h) { h->f->rperiod = 64; h->f->rphase = 0; h->f->rthold = 32; switch(h->ip[-1]){ case 0x19: /* RTHG */ h->f->rphase = 32; break; case 0x3D: /* RTDG */ h->f->rperiod = 32; h->f->rthold = 16; break; case 0x7C: /* RUTG */ h->f->rthold = 63; break; case 0x7D: /* RDTG */ h->f->rthold = 0; break; case 0x7A: /* ROFF */ h->f->rperiod = 0; break; } } static void h_sround(Hint *h) { u8int n; n = pop(h); if((n >> 6 & 3) == 3) herror(h, "(S)ROUND: period set to reserved value 3"); if(h->ip[-1] == 0x77) h->f->rperiod = 181 >> (2 - (n >> 6 & 3)); else h->f->rperiod = 32 << (n >> 6 & 3); h->f->rphase = h->f->rperiod * (n >> 4 & 3) / 4; if((n & 15) == 0) h->f->rthold = h->f->rperiod - 1; else h->f->rthold = h->f->rperiod * ((int)(n & 15) - 4) / 8; } static void h_srp(Hint *h) { h->f->rp[h->ip[-1] & 3] = pop(h); } static void h_szp(Hint *h) { int n, t; n = pop(h); if(n>>1 != 0) herror(h, "SZP invalid argument %d", n); t = h->ip[-1] - 0x13; if(t == 3) h->f->zp = 7 * n; else h->f->zp = h->f->zp & ~(1<f->pvx * p->x + h->f->pvy * p->y, 16384); return rounddiv(h->f->pvx * (p->x - q->x) + h->f->pvy * (p->y - q->y), 16384); } static int dualproject(Hint *h, TTPoint *p, TTPoint *q) { if(q == nil) return rounddiv(h->f->dpvx * p->x + h->f->dpvy * p->y, 16384); return rounddiv(h->f->dpvx * (p->x - q->x) + h->f->dpvy * (p->y - q->y), 16384); } static TTPoint forceproject(Hint *h, TTPoint p, int d) { TTFont *f; TTPoint n; int den; vlong k; f = h->f; den = f->pvx * f->fvx + f->pvy * f->fvy; if(den == 0) herror(h, "FV and PV orthogonal"); k = f->fvx * p.y - f->fvy * p.x; n.x = vrounddiv(16384LL * d * f->fvx - k * f->pvy, den); n.y = vrounddiv(16384LL * d * f->fvy + k * f->pvx, den); n.flags = p.flags; return n; } static void h_miap(Hint *h) { int a, pi, di, d, d0, d1; TTPoint p, n; a = h->ip[-1] & 1; di = pop(h); pi = pop(h); if((uint)di >= h->f->ncvt) herror(h, "MIAP out of range"); p = getpoint(h, ZP0, pi); d0 = h->f->cvt[di]; dprint("cvt %d\n", d0); d1 = project(h, &p, nil); dprint("old %d\n", d1); d = d0; if((h->f->zp & 1) != 0){ if(a && abs(d1 - d) > h->f->cvci) d = d1; }else{ /* fuck you microsoft */ h->f->twiorg[pi].x = rounddiv(d0 * h->f->pvx, 16384); h->f->twiorg[pi].y = rounddiv(d0 * h->f->pvy, 16384); } if(a) d = ttround(h, d); n = forceproject(h, p, d); setpoint(h, 0x80, pi, n); h->f->rp[0] = h->f->rp[1] = pi; } static void h_mdap(Hint *h) { int pi; TTPoint p; pi = pop(h); p = getpoint(h, ZP0, pi); if((h->ip[-1] & 1) != 0) p = forceproject(h, p, ttround(h, project(h, &p, nil))); setpoint(h, ZP0, pi, p); h->f->rp[0] = h->f->rp[1] = pi; } static void h_ip(Hint *h) { int i; int pi; TTPoint p1, op1, p2, op2, p, op, n; int dp1, dp2, do12, d; p1 = getpoint(h, RP1 | ZP0, 0); op1 = getpoint(h, RP1 | ZP0 | ORIG, 0); p2 = getpoint(h, RP2 | ZP1, 0); op2 = getpoint(h, RP2 | ZP1 | ORIG, 0); dp1 = project(h, &p1, nil); dp2 = project(h, &p2, nil); do12 = dualproject(h, &op1, &op2); if(do12 == 0) herror(h, "invalid IP[] call"); for(i = 0; i < h->f->loop; i++){ pi = pop(h); p = getpoint(h, ZP2, pi); op = getpoint(h, ZP2 | ORIG, pi); d = ttfvrounddiv((vlong)dp1 * dualproject(h, &op, &op2) - (vlong)dp2 * dualproject(h, &op, &op1), do12); n = forceproject(h, p, d); setpoint(h, 0x82, pi, n); dprint("(%d,%d) -> (%d,%d)\n", p.x, p.y, n.x, n.y); } h->f->loop = 1; } static void h_gc0(Hint *h) { int pi; TTPoint p; pi = pop(h); p = getpoint(h, ZP2, pi); push(h, project(h, &p, nil)); } static void h_gc1(Hint *h) { int pi; TTPoint p; pi = pop(h); p = getpoint(h, ZP2|ORIG, pi); push(h, dualproject(h, &p, nil)); } static void h_wcvtp(Hint *h) { u32int v, l; v = pop(h); l = pop(h); if(l >= h->f->ncvt) herror(h, "WCVTP out of range"); h->f->cvt[l] = v; } static void h_wcvtf(Hint *h) { u32int v, l; v = pop(h); l = pop(h); if(l >= h->f->ncvt) herror(h, "WCVTF out of range"); h->f->cvt[l] = rounddiv(v * h->f->ppem * 64, h->f->u->emsize); } static void h_rcvt(Hint *h) { u32int l; l = pop(h); if(l >= h->f->ncvt) herror(h, "RCVT out of range"); push(h, h->f->cvt[l]); } static void h_round(Hint *h) { push(h, ttround(h, pop(h))); } static void h_roll(Hint *h) { u32int a, b, c; a = pop(h); b = pop(h); c = pop(h); push(h, b); push(h, a); push(h, c); } static void h_pop(Hint *h) { pop(h); } static void h_clear(Hint *h) { h->sp = 0; } static void h_depth(Hint *h) { push(h, h->sp); } static void h_scvtci(Hint *h) { h->f->cvci = pop(h); } static void h_mirp(Hint *h) { int a; u32int cvti, pi; TTPoint n, p, p0, op, op0; int d0, d; a = h->ip[-1] & 31; cvti = pop(h); pi = pop(h); if(cvti >= h->f->ncvt) herror(h, "MIRP out of bounds"); d = h->f->cvt[cvti]; dprint("cvt %d\n", d); if(abs(d - h->f->singlewval) < h->f->singlewci) d = d < 0 ? -h->f->singlewci : h->f->singlewci; dprint("single %d\n", d); p = getpoint(h, ZP1, pi); p0 = getpoint(h, ZP0 | RP0, 0); op = getpoint(h, ZP1 | ORIG, pi); op0 = getpoint(h, ZP0 | RP0 | ORIG, 0); d0 = dualproject(h, &op, &op0); if(h->f->autoflip && (d0 ^ d) < 0) d = -d; if((a & 4) != 0){ if((h->f->zp + 1 & 3) <= 1 && abs(d - d0) > h->f->cvci) d = d0; dprint("cutin %d (%d)\n", d, h->f->cvci); d = ttround(h, d); } dprint("round %d\n", d); if((a & 8) != 0) if(d0 >= 0){ if(d < h->f->mindist) d = h->f->mindist; }else{ if(d > -h->f->mindist) d = -h->f->mindist; } dprint("mindist %d (%d)\n", d, h->f->mindist); d += project(h, &p0, nil); dprint("total %d\n", d); n = forceproject(h, p, d); setpoint(h, ZP1, pi, n); h->f->rp[1] = h->f->rp[0]; h->f->rp[2] = pi; if((a & 16) != 0) h->f->rp[0] = pi; } static void h_msirp(Hint *h) { int a; u32int pi; TTPoint n, p, p0; int d; a = h->ip[-1] & 31; d = pop(h); pi = pop(h); if(abs(d - h->f->singlewval) < h->f->singlewci) d = d < 0 ? -h->f->singlewci : h->f->singlewci; p = getpoint(h, ZP1, pi); p0 = getpoint(h, ZP0 | RP0, 0); d += project(h, &p0, nil); n = forceproject(h, p, d); setpoint(h, ZP1, pi, n); h->f->rp[1] = h->f->rp[0]; h->f->rp[2] = pi; if((a & 1) != 0) h->f->rp[0] = pi; } static void h_deltac(Hint *h) { int n, b, c, arg; n = pop(h); b = (h->ip[-1] - 0x73) * 16 + h->f->deltabase; while(n--){ c = pop(h); arg = pop(h); if(h->f->ppem != b + (arg >> 4)) continue; arg &= 0xf; arg = arg + (arg >> 3) - 8 << h->f->deltashift; if((uint)c >= h->f->ncvt) herror(h, "DELTAC argument out of range"); h->f->cvt[c] += arg; } } static void h_deltap(Hint *h) { int cnt, b, pi, arg; TTPoint p, n; cnt = pop(h); b = (h->ip[-1] == 0x5d ? 0 : h->ip[-1] - 0x70) * 16 + h->f->deltabase; while(cnt--){ pi = pop(h); arg = pop(h); if(h->f->ppem != b + (arg >> 4)) continue; arg &= 0xf; arg = arg + (arg >> 3) - 8 << h->f->deltashift; p = getpoint(h, ZP0, pi); n = forceproject(h, p, project(h, &p, nil) + arg); setpoint(h, ZP0, pi, n); } } static void h_jmpr(Hint *h) { h->ip += (int)pop(h) - 1; if(h->ip < h->shint || h->ip > h->ehint) herror(h, "JMPR out of bounds"); } static void h_jrcond(Hint *h) { u32int e; int n; e = pop(h); n = pop(h) - 1; if((e == 0) == (h->ip[-1] & 1)){ h->ip += n; if(h->ip < h->shint || h->ip > h->ehint) herror(h, "JROT/JROF out of bounds"); } } static void h_smd(Hint *h) { h->f->mindist = pop(h); } static void h_alignrp(Hint *h) { int i, pi; TTPoint p, q, n; int dq; q = getpoint(h, ZP0 | RP0, 0); dq = project(h, &q, nil); for(i = 0; i < h->f->loop; i++){ pi = pop(h); p = getpoint(h, ZP1, pi); n = forceproject(h, p, dq); setpoint(h, ZP1, pi, n); } h->f->loop = 1; } static TTPoint dirvec(TTPoint a, TTPoint b) { TTPoint r; double d; r.x = a.x - b.x; r.y = a.y - b.y; if(r.x == 0 && r.y == 0) r.x = 1<<14; else{ d = hypot(r.x, r.y); r.x = r.x / d * 16384; r.y = r.y / d * 16384; } return r; } static void h_sxvtl(Hint *h) { int pi1, pi2; TTPoint p1, p2; TTPoint p; int z; pi2 = pop(h); pi1 = pop(h); p1 = getpoint(h, ZP1, pi1); p2 = getpoint(h, ZP2, pi2); p = dirvec(p1, p2); if((h->ip[-1] & 1) != 0){ z = p.x; p.x = -p.y; p.y = z; } if(h->ip[-1] >= 8){ h->f->fvx = p.x; h->f->fvy = p.y; }else{ h->f->dpvx = h->f->pvx = p.x; h->f->dpvy = h->f->pvy = p.y; } } static void h_sfvfs(Hint *h) { h->f->fvy = pop(h); h->f->fvx = pop(h); } static void h_spvfs(Hint *h) { h->f->dpvy = h->f->pvy = pop(h); h->f->dpvx = h->f->pvx = pop(h); } static void h_gfv(Hint *h) { push(h, h->f->fvx); push(h, h->f->fvy); } static void h_gpv(Hint *h) { push(h, h->f->pvx); push(h, h->f->pvy); } static void h_mdrp(Hint *h) { int pi; TTPoint p, p0, op, op0, n; int d, d0; pi = pop(h); p = getpoint(h, ZP1, pi); p0 = getpoint(h, ZP0 | RP0, 0); op = getpoint(h, ZP1 | ORIG, pi); op0 = getpoint(h, ZP0 | RP0 | ORIG, 0); d = d0 = dualproject(h, &op, &op0); if(abs(d - h->f->singlewval) < h->f->singlewci) d = d >= 0 ? -h->f->singlewci : h->f->singlewci; if((h->ip[-1] & 4) != 0) d = ttround(h, d); if((h->ip[-1] & 8) != 0) if(d0 >= 0){ if(d < h->f->mindist) d = h->f->mindist; }else{ if(d > -h->f->mindist) d = -h->f->mindist; } n = forceproject(h, p, d + project(h, &p0, nil)); setpoint(h, ZP1, pi, n); h->f->rp[1] = h->f->rp[0]; h->f->rp[2] = pi; if((h->ip[-1] & 16) != 0) h->f->rp[0] = pi; } static void h_sdpvtl(Hint *h) { int pi1, pi2; TTPoint p1, p2; TTPoint op1, op2; TTPoint p; pi2 = pop(h); pi1 = pop(h); p1 = getpoint(h, ZP1, pi1); p2 = getpoint(h, ZP2, pi2); op1 = getpoint(h, ZP1 | ORIG, pi1); op2 = getpoint(h, ZP2 | ORIG, pi2); p = dirvec(p1, p2); if((h->ip[-1] & 1) != 0){ h->f->pvx = -p.y; h->f->pvy = p.x; }else{ h->f->pvx = p.x; h->f->pvy = p.y; } p = dirvec(op1, op2); if((h->ip[-1] & 1) != 0){ h->f->dpvx = -p.y; h->f->dpvy = p.x; }else{ h->f->dpvx = p.x; h->f->dpvy = p.y; } } static void h_sfvtpv(Hint *h) { h->f->fvx = h->f->pvx; h->f->fvy = h->f->pvy; } static void h_sdb(Hint *h) { h->f->deltabase = pop(h); } static void h_sds(Hint *h) { h->f->deltashift = pop(h); } static void h_ssw(Hint *h) { h->f->singlewval = pop(h); } static void h_sswci(Hint *h) { h->f->singlewci = pop(h); } static void h_fliponoff(Hint *h) { h->f->autoflip = h->ip[-1] & 1; } static void h_md0(Hint *h) { TTPoint p0, p1; p1 = getpoint(h, ZP1, pop(h)); p0 = getpoint(h, ZP0, pop(h)); push(h, project(h, &p0, &p1)); } static void h_md1(Hint *h) { TTPoint p0, p1; p1 = getpoint(h, ZP1 | ORIG, pop(h)); p0 = getpoint(h, ZP0 | ORIG, pop(h)); push(h, dualproject(h, &p0, &p1)); } static void h_shpix(Hint *h) { int i, d, pi, dx, dy; TTPoint p; d = pop(h); dx = vrounddiv((vlong)h->f->fvx * d, 16384); dy = vrounddiv((vlong)h->f->fvy * d, 16384); for(i = 0; i < h->f->loop; i++){ pi = pop(h); p = getpoint(h, ZP2, pi); p.x += dx; p.y += dy; setpoint(h, ZP2, pi, p); } h->f->loop = 1; } static void iup1(Hint *h, int ip, int iq, int i, int e) { TTGlyph *g; int z; g = h->g; if(g->ptorg[ip].x == g->ptorg[iq].x) for(; i <= e; i++) g->pt[i].x = g->ptorg[i].x + g->pt[iq].x - g->ptorg[iq].x; else for(; i <= e; i++){ z = (g->ptorg[i].x - g->ptorg[iq].x) * 64 / (g->ptorg[ip].x - g->ptorg[iq].x); if(z < 0) z = 0; else if(z > 64) z = 64; g->pt[i].x = g->ptorg[i].x + (((g->pt[ip].x - g->ptorg[ip].x) * z + (g->pt[iq].x - g->ptorg[iq].x) * (64 - z)) / 64); } } static void iup0(Hint *h, int ip, int iq, int i, int e) { TTGlyph *g; int z; g = h->g; if(g->ptorg[ip].y == g->ptorg[iq].y) for(; i <= e; i++) g->pt[i].y = g->ptorg[i].y + g->pt[iq].y - g->ptorg[iq].y; else for(; i <= e; i++){ z = (g->ptorg[i].y - g->ptorg[iq].y) * 64 / (g->ptorg[ip].y - g->ptorg[iq].y); if(z < 0) z = 0; else if(z > 64) z = 64; g->pt[i].y = g->ptorg[i].y + (((g->pt[ip].y - g->ptorg[ip].y) * z + (g->pt[iq].y - g->ptorg[iq].y) * (64 - z)) / 64); } } static void h_iup(Hint *h) { int i, j, t0, t1; TTPoint *p; void (*iupp)(Hint *, int, int, int, int); iupp = (h->ip[-1] & 1) != 0 ? iup1 : iup0; for(i = 0; i < h->g->ncon; i++){ t0 = t1 = -1; for(j = h->g->confst[i]; j < h->g->confst[i+1]; j++){ p = &h->g->pt[j]; if((p->flags & TOUCHY>>(h->ip[-1]&1)) != 0){ if(t0 < 0) t0 = j; if(t1 >= 0) iupp(h, t1, j, t1 + 1, j - 1); t1 = j; } } if(t1 != t0){ iupp(h, t1, t0, h->g->confst[i], t0 - 1); iupp(h, t1, t0, t1 + 1, h->g->confst[i+1]-1); }else if(t0 >= 0) iupp(h, t0, t0, h->g->confst[i], h->g->confst[i+1]-1); } for(i = 0; i < h->g->npt; i++) dprint("%d: %+π\n", i, h->g->pt[i]); } static void h_sloop(Hint *h) { int n; n = pop(h); if(n <= 0) herror(h, "SLOOP invalid argument %d", n); h->f->loop = n; } static void h_scfs(Hint *h) { int d, pi; TTPoint p, n; d = pop(h); pi = pop(h); p = getpoint(h, ZP2, pi); n = forceproject(h, p, d); setpoint(h, ZP2, pi, n); } static void h_fliprg(Hint *h) { int i, e; e = pop(h); i = pop(h); if(h->g == nil) herror(h, "FLIPRG without glyph"); for(; i <= e; i++) if((int)i < h->g->npt) h->g->pt[i].flags = h->g->pt[i].flags & ~1 | h->ip[-1] & 1; } static void h_isect(Hint *h) { int a0i, a1i, b0i, b1i, pi; TTPoint a0, a1, b0, b1, p; int n0x, n0y; vlong n0c; int n1x, n1y; vlong n1c; int Δ; a0i = pop(h); a1i = pop(h); b0i = pop(h); b1i = pop(h); pi = pop(h); a0 = getpoint(h, ZP0, a0i); a1 = getpoint(h, ZP0, a1i); b0 = getpoint(h, ZP1, b0i); b1 = getpoint(h, ZP1, b1i); p = getpoint(h, ZP2, pi); n0x = a1.y - a0.y; n0y = a0.x - a1.x; n0c = (vlong)n0x * a0.x + (vlong)n0y * a0.y; n1x = b1.y - b0.y; n1y = b0.x - b1.x; n1c = (vlong)n1x * b0.x + (vlong)n1y * b0.y; Δ = (vlong)n1x * n0y - (vlong)n0x * n1y; if(Δ == 0){ p.x = ((a0.x + a1.x) / 2 + (b0.x + b1.x) / 2) / 2; p.y = ((a0.y + a1.y) / 2 + (b0.y + b1.y) / 2) / 2; }else{ p.x = vrounddiv(n0y * n1c - n1y * n0c, Δ); p.y = vrounddiv(n1x * n0c - n0x * n1c, Δ); } p.flags |= TOUCH; setpoint(h, ZP2, pi, p); } static void h_shp(Hint *h) { int i; TTPoint rp, orp; int pi; TTPoint p, n; int d, dp; if((h->ip[-1] & 1) != 0){ rp = getpoint(h, RP1|ZP0, 0); orp = getpoint(h, RP1|ZP0|ORIG, 0); }else{ rp = getpoint(h, RP2|ZP1, 0); orp = getpoint(h, RP2|ZP1|ORIG, 0); } d = project(h, &rp, &orp); for(i = 0; i < h->f->loop; i++){ pi = pop(h); p = getpoint(h, ZP2, pi); dp = project(h, &p, nil); n = forceproject(h, p, dp + d); setpoint(h, ZP2, pi, n); } h->f->loop = 1; } static void h_shc(Hint *h) { int i, c; int rpi; TTPoint rp, orp; TTPoint p, n; int d, dp; if((h->ip[-1] & 1) != 0){ rpi = h->f->rp[1]; if(((h->f->zp ^ h->f->zp >> 2) & 1) != 0) rpi = -1; rp = getpoint(h, RP1|ZP0, 0); orp = getpoint(h, RP1|ZP0|ORIG, 0); }else{ rpi = h->f->rp[2]; if(((h->f->zp ^ h->f->zp >> 1) & 1) != 0) rpi = -1; rp = getpoint(h, RP2|ZP1, 0); orp = getpoint(h, RP2|ZP1|ORIG, 0); } c = pop(h); if(h->g == nil) herror(h, "SHC[] outside of glyf program"); if((uint)c >= h->g->ncon) herror(h, "contour %d out of range", c); d = project(h, &rp, &orp); for(i = h->g->confst[c]; i < h->g->confst[c+1]; i++){ if(i == rpi) continue; p = getpoint(h, ZP2, i); dp = project(h, &p, nil); n = forceproject(h, p, dp + d); setpoint(h, ZP2, i, n); } } static void h_shz(Hint *h) { int i, e, np; TTPoint rp, orp; TTPoint p, n; int d, dp; if((h->ip[-1] & 1) != 0){ rp = getpoint(h, RP1|ZP0, 0); orp = getpoint(h, RP1|ZP0|ORIG, 0); }else{ rp = getpoint(h, RP2|ZP1, 0); orp = getpoint(h, RP2|ZP1|ORIG, 0); } e = pop(h); if((uint)e > 1) herror(h, "SHZ[] with invalid zone %d", e); d = project(h, &rp, &orp); np = e ? h->g->npt : h->f->u->maxTwilightPoints; for(i = 0; i < np; i++){ p = getpointz(h, e, i); dp = project(h, &p, nil); n = forceproject(h, p, dp + d); setpointz(h, e, i, n); } } static void (*itable[256])(Hint *) = { [0x00] h_svtca, h_svtca, h_svtca, h_svtca, h_svtca, h_svtca, [0x06] h_sxvtl, h_sxvtl, h_sxvtl, h_sxvtl, [0x0a] h_spvfs, [0x0b] h_sfvfs, [0x0c] h_gpv, [0x0d] h_gfv, [0x0e] h_sfvtpv, [0x0f] h_isect, [0x10] h_srp, h_srp, h_srp, [0x13] h_szp, h_szp, h_szp, h_szp, [0x17] h_sloop, [0x18] h_roundst, h_roundst, [0x1a] h_smd, [0x1b] h_else, [0x1c] h_jmpr, [0x1d] h_scvtci, [0x1e] h_sswci, [0x1f] h_ssw, [0x20] h_dup, [0x21] h_pop, [0x22] h_clear, [0x23] h_swap, [0x24] h_depth, [0x25] h_cindex, [0x26] h_mindex, [0x2a] h_loopcall, [0x2b] h_call, [0x2c] h_fdef, [0x2e] h_mdap, h_mdap, [0x30] h_iup, h_iup, [0x32] h_shp, h_shp, [0x34] h_shc, h_shc, [0x36] h_shz, h_shz, [0x38] h_shpix, [0x39] h_ip, [0x3a] h_msirp, h_msirp, [0x3c] h_alignrp, [0x3d] h_roundst, [0x3e] h_miap, h_miap, [0x40] h_npushb, [0x41] h_npushw, [0x42] h_ws, [0x43] h_rs, [0x44] h_wcvtp, [0x45] h_rcvt, [0x46] h_gc0, h_gc1, [0x48] h_scfs, [0x49] h_md0, h_md1, [0x4b] h_mppem, [0x4d] h_fliponoff, h_fliponoff, [0x4f] h_nop, [0x50] h_binop, h_binop, h_binop, h_binop, h_binop, h_binop, [0x56] h_unop, h_unop, [0x58] h_if, [0x59] h_nop, /* endif */ [0x5a] h_binop, h_binop, [0x5c] h_unop, [0x5d] h_deltap, [0x5e] h_sdb, [0x5f] h_sds, [0x60] h_binop, h_binop, h_binop, h_binop, h_unop, h_unop, h_unop, h_unop, [0x68] h_unop, h_unop, h_unop, h_unop, h_nop, h_nop, h_nop, h_nop, [0x70] h_wcvtf, [0x71] h_deltap, h_deltap, [0x73] h_deltac, h_deltac, h_deltac, [0x76] h_sround, h_sround, [0x78] h_jrcond, h_jrcond, [0x7a] h_roundst, [0x7c] h_roundst, h_roundst, [0x7e] h_pop, [0x7f] h_pop, [0x81] h_fliprg, h_fliprg, [0x85] h_scanctrl, [0x86] h_sdpvtl, h_sdpvtl, [0x88] h_getinfo, [0x8a] h_roll, [0x8b] h_binop, h_binop, [0x8d] h_scantype, [0x8e] h_instctrl, [0xb0] h_pushb, h_pushb, h_pushb, h_pushb, h_pushb, h_pushb, h_pushb, h_pushb, [0xb8] h_pushw, h_pushw, h_pushw, h_pushw, h_pushw, h_pushw, h_pushw, h_pushw, [0xc0] h_mdrp, h_mdrp, h_mdrp, h_mdrp, h_mdrp, h_mdrp, h_mdrp, h_mdrp, h_mdrp, h_mdrp, h_mdrp, h_mdrp, h_mdrp, h_mdrp, h_mdrp, h_mdrp, h_mdrp, h_mdrp, h_mdrp, h_mdrp, h_mdrp, h_mdrp, h_mdrp, h_mdrp, h_mdrp, h_mdrp, h_mdrp, h_mdrp, h_mdrp, h_mdrp, h_mdrp, h_mdrp, [0xe0] h_mirp, h_mirp, h_mirp, h_mirp, h_mirp, h_mirp, h_mirp, h_mirp, h_mirp, h_mirp, h_mirp, h_mirp, h_mirp, h_mirp, h_mirp, h_mirp, h_mirp, h_mirp, h_mirp, h_mirp, h_mirp, h_mirp, h_mirp, h_mirp, h_mirp, h_mirp, h_mirp, h_mirp, h_mirp, h_mirp, h_mirp, h_mirp, }; static int pointfmt(Fmt *f) { TTPoint p; p = va_arg(f->args, TTPoint); if((f->flags & FmtSign) != 0) return fmtprint(f, "(%.2f,%.2f,%d)", (float)p.x/64, (float)p.y/64, p.flags); else return fmtprint(f, "(%d,%d,%d)", p.x, p.y, p.flags); } static void run(Hint *h) { while(h->ip < h->ehint){ if(debug) debugprint(h, 0); if(itable[*h->ip] == nil) sysfatal("unknown hint instruction %#.2x", *h->ip); else itable[*h->ip++](h); } } static int runpg(TTFont *f, TTGlyph *g, uchar *buf, int n) { Hint h; static int didfmt; if(debug && !didfmt){ fmtinstall(L'π', pointfmt); didfmt = 1; } memset(&h, 0, sizeof(Hint)); if(setjmp(h.jmp) != 0){ errstr(h.err, sizeof(h.err)); return -1; } h.g = g; h.f = f; h.stack = f->hintstack; h.nstack = f->u->maxStackElements; h.ip = h.shint = buf; h.ehint = buf + n; run(&h); return 0; } int ttfhint(TTGlyph *g) { int rc, i; if((g->font->defstate.instctrl & 1<<1) != 0) return 0; dprint("HINT:\n"); if((g->font->defstate.instctrl & 1<<2) != 0) g->font->TTGState = defstate; else g->font->TTGState = g->font->defstate; rc = runpg(g->font, g, g->hint, g->nhint); if(debug && rc >= 0){ for(i = 0; i < g->npt; i++) dprint("%d: %+π\n", i, g->pt[i]); } return rc; } int ttfrunfpgm(TTFont *f) { int len, rc; u8int *buf; f->TTGState = defstate; f->defstate = defstate; len = ttfgototable(f->u, "fpgm"); if(len <= 0) return 0; buf = mallocz(len, 1); if(buf == nil) return -1; Bread(f->u->bin, buf, len); dprint("FPGM:\n"); rc = runpg(f, nil, buf, len); free(buf); return rc; } int ttfruncvt(TTFont *f) { int len, rc; u8int *buf; f->TTGState = defstate; f->defstate = defstate; len = ttfgototable(f->u, "prep"); if(len <= 0) return 0; buf = mallocz(len, 1); if(buf == nil) return -1; Bread(f->u->bin, buf, len); dprint("CVT:\n"); rc = runpg(f, nil, buf, len); free(buf); if(rc >= 0){ f->zp = 7; f->rp[0] = 0; f->rp[1] = 0; f->rp[2] = 0; f->loop = 1; f->rperiod = 64; f->rphase = 0; f->rthold = 32; f->fvx = 16384; f->fvy = 0; f->pvx = 16384; f->pvy = 0; f->dpvx = 16384; f->dpvy = 0; f->defstate = f->TTGState; } return rc; }