]> git.lizzy.rs Git - plan9front.git/blob - sys/src/libttf/hint.c
libttf: instruction 0x24 is DEPTH, not CLEAR
[plan9front.git] / sys / src / libttf / hint.c
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <ttf.h>
5 #include "impl.h"
6
7 typedef struct Hint Hint;
8
9 enum { debug = 0 };
10
11 #pragma varargck type "π" TTPoint
12
13 #define dprint(...) {if(debug) fprint(2, __VA_ARGS__);}
14
15 static TTGState defstate = {
16         .fvx = 16384,
17         .fvy = 0,
18         .pvx = 16384,
19         .pvy = 0,
20         .dpvx = 16384,
21         .dpvy = 0,
22         .instctrl = 0,
23         .scanctrl = 0,
24         .rperiod = 64,
25         .rphase = 0,
26         .rthold = 32,
27         .zp = 7,
28         .cvci = 68,
29         .loop = 1,
30         .singlewval = 0,
31         .singlewci = 0,
32         .deltabase = 9,
33         .deltashift = 3,
34         .autoflip = 1,
35         .mindist = 64,
36 };
37
38 struct Hint {
39         TTFont *f;
40         TTGlyph *g;
41         u8int *shint, *ip, *ehint;
42         u32int *stack;
43         int sp, nstack;
44         int level;
45         char err[ERRMAX];
46         jmp_buf jmp;
47 };
48
49 int
50 rounddiv(int a, int b)
51 {
52         if(b < 0){ a = -a; b = -b; }
53         if(a > 0)
54                 return (a + b/2) / b;
55         else
56                 return (a - b/2) / b;
57 }
58
59 int
60 vrounddiv(vlong a, int b)
61 {
62         if(b < 0){ a = -a; b = -b; }
63         if(a > 0)
64                 return (a + b/2) / b;
65         else
66                 return (a - b/2) / b;
67 }
68
69 static void
70 herror(Hint *h, char *fmt, ...)
71 {
72         va_list va;
73         
74         va_start(va, fmt);
75         vsnprint(h->err, sizeof(h->err), fmt, va);
76         va_end(va);
77         dprint("error: %s\n", h->err);
78         longjmp(h->jmp, 1);
79 }
80
81 static void
82 push(Hint *h, u32int w)
83 {
84         assert(h->sp < h->nstack);
85         h->stack[h->sp++] = w;
86 }
87
88 static u32int
89 pop(Hint *h)
90 {
91         assert(h->sp > 0);
92         return h->stack[--h->sp];
93 }
94
95 static u8int
96 fetch8(Hint *h)
97 {
98         if(h->ip == h->ehint)
99                 herror(h, "missing byte");
100         return *h->ip++;
101 }
102
103 enum {
104         RP0 = 0x10,
105         RP1 = 0x20,
106         RP2 = 0x30,
107         ZP0 = 0,
108         ZP1 = 0x100,
109         ZP2 = 0x200,
110         ORIG = 0x1000,
111         NOTOUCH = 0x2000,
112 };
113
114 static TTPoint
115 getpoint(Hint *h, int n, int pi)
116 {
117         if((n & RP2) != 0)
118                 pi = h->f->rp[(n >> 4 & 3) - 1];
119         if((h->f->zp >> (n >> 8 & 3) & 1) != 0){
120                 if(h->g == nil)
121                         herror(h, "access to glyph zone from FPGM/CVT");
122                 if((uint)pi >= h->g->npt)
123                         herror(h, "glyph zone point index %d out of range", n);
124                 dprint("G%s%d: %+π\n", n&ORIG?"O":"", pi, (n & ORIG) != 0 ? h->g->ptorg[pi] : h->g->pt[pi]);
125                 return (n & ORIG) != 0 ? h->g->ptorg[pi] : h->g->pt[pi];
126         }else{
127                 if((uint)pi >= h->f->u->maxTwilightPoints)
128                         herror(h, "twilight zone point index %d out of range", pi);
129                 return (n & ORIG) != 0 ? h->f->twiorg[pi] : h->f->twilight[pi];
130         }
131 }
132
133 static void
134 setpoint(Hint *h, int n, int pi, TTPoint p)
135 {
136         if((n & RP2) != 0)
137                 pi = h->f->rp[(n >> 4 & 3) - 1];
138         if((n & NOTOUCH) == 0){
139                 if(h->f->fvx != 0) p.flags |= 2;
140                 if(h->f->fvy != 0) p.flags |= 4;
141         }
142         if((h->f->zp >> (n >> 8 & 3) & 1) != 0){
143                 if(h->g == nil)
144                         herror(h, "access to glyph zone from FPGM/CVT");
145                 if((uint)pi >= h->g->npt)
146                         herror(h, "glyph zone point index %d out of range", n);
147                 dprint("G%d: %+π -> %+π\n", pi, h->g->pt[pi], p);
148                 h->g->pt[pi] = p;
149         }else{
150                 if((uint)pi >= h->f->u->maxTwilightPoints)
151                         herror(h, "twilight zone point index %d out of range", n);
152                 dprint("T%d: %+π -> %+π\n", pi, h->f->twilight[pi], p);
153                 h->f->twilight[pi] = p;
154         }
155 }
156
157 static void
158 debugprint(Hint *h, int skip)
159 {
160         Fmt f;
161         char buf[256];
162
163         static char *opcnames[256] = {
164                 [0x00] "SVTCA", "SVTCA", "SPVTCA", "SPVTCA", "SFVTCA", "SFVTCA", "SPVTL", "SPVTL",
165                 [0x08] "SFVTL", "SFVTL", "SPVFS", "SFVFS", "GPV", "GFV", "SFVTPV", "ISECT",
166                 [0x10] "SRP0", "SRP1", "SRP2", "SZP0", "SZP1", "SZP2", "SZPS", "SLOOP",
167                 [0x18] "RTG", "RTHG", "SMD", "ELSE", "JMPR", "SCVTCI", "SSWCI", "SSW",
168                 [0x20] "DUP", "POP", "CLEAR", "SWAP", "DEPTH", "CINDEX", "MINDEX", "ALIGNPTS",
169                 [0x28] nil, "UTP", "LOOPCALL", "CALL", "FDEF", "ENDF", "MDAP", "MDAP",
170                 [0x30] "IUP", "IUP", "SHP", "SHP", "SHC", "SHC", "SHZ", "SHZ",
171                 [0x38] "SHPIX", "IP", "MSIRP", "MSIRP", "ALIGNRP", "RTDG", "MIAP", "MIAP",
172                 [0x40] "NPUSHB", "NPUSHW", "WS", "RS", "WCVTP", "RCVT", "GC", "GC",
173                 [0x48] "SCFS", "MD", "MD", "MPPEM", "MPS", "FLIPON", "FLIPOFF", "DEBUG",
174                 [0x50] "LT", "LTEQ", "GT", "GTEQ", "EQ", "NEQ", "ODD", "EVEN",
175                 [0x58] "IF", "EIF", "AND", "OR", "NOT", "DELTAP1", "SDB", "SDS",
176                 [0x60] "ADD", "SUB", "DIV", "MUL", "ABS", "NEG", "FLOOR", "CEILING",
177                 [0x68] "ROUND", "ROUND", "ROUND", "ROUND", "NROUND", "NROUND", "NROUND", "NROUND",
178                 [0x70] "WCVTF", "DELTAP2", "DELTAP3", "DELTAC1", "DELTAC2", "DELTAC3", "SROUND", "S45ROUND",
179                 [0x78] "JROT", "JROF", "ROFF", nil, "RUTG", "RDTG", "SANGW", "AA",
180                 [0x80] "FLIPPT", "FLIPRGON", "FLIPRGOFF", [0x85] "SCANCTRL", "SDPVTL", "SDPVTL",
181                 [0x88] "GETINFO", "IDEF", "ROLL", "MAX", "MIN", "SCANTYPE", "INSTCTRL", nil,
182                 [0xB0] "PUSHB", "PUSHB", "PUSHB", "PUSHB", "PUSHB", "PUSHB", "PUSHB", "PUSHB", 
183                 [0xB8] "PUSHW", "PUSHW", "PUSHW", "PUSHW", "PUSHW", "PUSHW", "PUSHW", "PUSHW",
184         };
185         static u8int argb[256] = {
186                 [0x00] 1, 1, 1, 1, 1, 1, 1, 1,
187                 [0x08] 1, 1, 1, 1, 1, 1,
188                 [0x2e] 1, 1,
189                 [0x30] 1, 1, 1, 1, 1, 1, 1, 1,
190                 [0x38] 0, 0, 1, 1, 0, 0, 1, 1,
191                 [0x46] 1, 1, 0, 1, 1,
192                 [0x68] 2, 2, 2, 2, 2, 2, 2, 2,
193         };
194         u8int op;
195         int i;
196
197         fmtfdinit(&f, 2, buf, sizeof(buf));
198         op = *h->ip;
199         if(skip) fmtprint(&f, "** ");
200         fmtprint(&f, "%d %d ", h->level, (int)(h->ip - h->shint));
201         if(op >= 0xc0)
202                 fmtprint(&f, "%s[%d]", op >= 0xe0 ? "MIRP" : "MDRP", op & 0x1f);
203         else if(opcnames[op] == nil)
204                 fmtprint(&f, "???");
205         else
206                 fmtprint(&f, argb[op] != 0 ? "%s[%d]" : "%s[]", opcnames[op], op & (1<<argb[op]) - 1);
207         if(!skip){
208                 fmtprint(&f, " :: ");
209                 for(i = 0; i < 8 && i < h->sp; i++)
210                         fmtprint(&f, "%d ", h->stack[h->sp - 1 - i]);
211         }
212         fmtprint(&f, "\n");
213         fmtfdflush(&f);
214 }
215
216 static void
217 h_npushb(Hint *h)
218 {
219         u8int n, b;
220         
221         n = fetch8(h);
222         while(n-- > 0){
223                 b = fetch8(h);
224                 push(h, b);
225         }
226 }
227
228 static void
229 h_npushw(Hint *h)
230 {
231         u8int n;
232         u32int x;
233         
234         n = fetch8(h);
235         while(n-- > 0){
236                 x = fetch8(h) << 8;
237                 x |= fetch8(h);
238                 push(h, (short)x);
239         }
240 }
241
242 static void
243 h_pushb(Hint *h)
244 {
245         int n;
246         u8int b;
247         
248         n = (h->ip[-1] & 7) + 1;
249         while(n-- > 0){
250                 b = fetch8(h);
251                 push(h, b);
252         }
253 }
254
255 static void
256 h_pushw(Hint *h)
257 {
258         int n;
259         u16int w;
260         
261         n = (h->ip[-1] & 7) + 1;
262         while(n-- > 0){
263                 w = fetch8(h) << 8;
264                 w |= fetch8(h);
265                 push(h, (short)w);
266         }
267 }
268
269 static void
270 skip(Hint *h, int mode)
271 {
272         int level;
273
274         level = 0;
275         for(;;){
276                 if(h->ip >= h->ehint)
277                         herror(h, "reached end of stream during skip()");
278                 if(debug) debugprint(h, 1);
279                 switch(mode){
280                 case 0:
281                         if(*h->ip == 0x2d)
282                                 return;
283                         break;
284                 case 1:
285                         if(level == 0 && (*h->ip == 0x1b || *h->ip == 0x59))
286                                 return;
287                 }
288                 switch(*h->ip++){
289                 case 0x40:
290                 case 0x41:
291                         if(h->ip < h->ehint)
292                                 h->ip += *h->ip + 1;
293                         break;
294                 case 0x58: level++; break;
295                 case 0x59: level--; break;
296                 case 0xb0: case 0xb1: case 0xb2: case 0xb3:
297                 case 0xb4: case 0xb5: case 0xb6: case 0xb7:
298                         h->ip += (h->ip[-1] & 7) + 1;
299                         break;
300                 case 0xb8: case 0xb9: case 0xba: case 0xbb:
301                 case 0xbc: case 0xbd: case 0xbe: case 0xbf:
302                         h->ip += 2 * ((h->ip[-1] & 7) + 1);
303                         break;
304                 }
305         }
306 }
307
308 static void
309 h_fdef(Hint *h)
310 {
311         int i;
312         u8int *sp;
313         TTFont *f;
314         
315         f = h->f;
316         i = pop(h);
317         if((uint)i >= h->f->u->maxFunctionDefs)
318                 herror(h, "function identifier out of range");
319         sp = h->ip;
320         skip(h, 0);
321         f->func[i].npgm = h->ip - sp;
322         f->func[i].pgm = mallocz(f->func[i].npgm, 1);
323         if(f->func[i].pgm == nil)
324                 herror(h, "malloc: %r");
325         memcpy(f->func[i].pgm, sp, f->func[i].npgm);
326         h->ip++;
327 }
328
329 static void run(Hint *);
330
331 static void
332 h_call(Hint *h)
333 {
334         int i;
335         u8int *lip, *lshint, *lehint;
336         
337         i = pop(h);
338         if((uint)i >= h->f->u->maxFunctionDefs || h->f->func[i].npgm == 0)
339                 herror(h, "undefined funcion %d", i);
340         lip = h->ip;
341         lshint = h->shint;
342         lehint = h->ehint;
343         h->ip = h->shint = h->f->func[i].pgm;
344         h->ehint = h->ip + h->f->func[i].npgm;
345         h->level++;
346         run(h);
347         h->level--;
348         h->ip = lip;
349         h->shint = lshint;
350         h->ehint = lehint;
351 }
352
353 static void
354 h_loopcall(Hint *h)
355 {
356         int i, n;
357         u8int *lip, *lshint, *lehint;
358         
359         i = pop(h);
360         n = pop(h);
361         if((uint)i >= h->f->u->maxFunctionDefs || h->f->func[i].npgm == 0)
362                 herror(h, "undefined funcion %d", i);
363         for(; n > 0; n--){
364                 lip = h->ip;
365                 lshint = h->shint;
366                 lehint = h->ehint;
367                 h->ip = h->shint = h->f->func[i].pgm;
368                 h->ehint = h->ip + h->f->func[i].npgm;
369                 h->level++;
370                 run(h);
371                 h->level--;
372                 h->ip = lip;
373                 h->shint = lshint;
374                 h->ehint = lehint;
375         }
376 }
377
378 static void
379 h_dup(Hint *h)
380 {
381         u32int x;
382         
383         x = pop(h);
384         push(h, x);
385         push(h, x);
386 }
387
388 static void
389 h_swap(Hint *h)
390 {
391         u32int x, y;
392         
393         x = pop(h);
394         y = pop(h);
395         push(h, x);
396         push(h, y);
397 }
398
399 static void
400 h_cindex(Hint *h)
401 {
402         int n;
403         
404         n = pop(h);
405         if(n <= 0 || n > h->sp)
406                 herror(h, "CINDEX[%d] out of range", n);
407         push(h, h->stack[h->sp - n]);
408 }
409
410 static void
411 h_mindex(Hint *h)
412 {
413         int n, x;
414         
415         n = pop(h);
416         if(n <= 0 || n > h->sp)
417                 herror(h, "MINDEX[%d] out of range", n);
418         x = h->stack[h->sp - n];
419         memmove(&h->stack[h->sp - n], &h->stack[h->sp - n + 1], (n - 1) * sizeof(u32int));
420         h->stack[h->sp - 1] = x;
421 }
422
423 static void
424 h_svtca(Hint *h)
425 {
426         int a;
427         
428         a = h->ip[-1];
429         if(a < 2 || a >= 4){
430                 h->f->fvx = 16384 * (a & 1);
431                 h->f->fvy = 16384 * (~a & 1);
432         }
433         if(a < 4){
434                 h->f->dpvx = h->f->pvx = 16384 * (a & 1);
435                 h->f->dpvy = h->f->pvy = 16384 * (~a & 1);
436         }
437 }
438
439 static void
440 h_instctrl(Hint *h)
441 {
442         int s, v;
443         
444         s = pop(h);
445         v = pop(h);
446         if(v != 0)
447                 h->f->instctrl |= 1<<s;
448         else
449                 h->f->instctrl &= ~(1<<s);
450 }
451
452 static void
453 h_mppem(Hint *h)
454 {
455         push(h, h->f->ppem);
456 }
457
458 static int
459 ttround(Hint *h, int x)
460 {
461         int y;
462         
463         if(h->f->rperiod == 0) return x;
464         if(x >= 0){
465                 y = x - h->f->rphase + h->f->rthold;
466                 y -= y % h->f->rperiod;
467                 y += h->f->rphase;
468                 if(y < 0) y = h->f->rphase;
469         }else{
470                 y = x + h->f->rphase - h->f->rthold;
471                 y -= y % h->f->rperiod;
472                 y -= h->f->rphase;
473                 if(y > 0) y = -h->f->rphase;
474         }
475         return y;
476 }
477
478 static void
479 h_binop(Hint *h)
480 {
481         int a, b, r;
482         
483         b = pop(h);
484         a = pop(h);
485         switch(h->ip[-1]){
486         case 0x50: r = a < b; break;
487         case 0x51: r = a <= b; break;
488         case 0x52: r = a > b; break;
489         case 0x53: r = a >= b; break;
490         case 0x54: r = a == b; break;
491         case 0x55: r = a != b; break;
492         case 0x5a: r = a && b; break;
493         case 0x5b: r = a || b; break;
494         case 0x60: r = a + b; break;
495         case 0x61: r = a - b; break;
496         case 0x62: if(b == 0) herror(h, "division by zero"); r = (vlong)(int)a * 64 / (int)b; break;
497         case 0x63: r = (vlong)(int)a * (vlong)(int)b >> 6; break;
498         case 0x8b: r = a < b ? b : a; break;
499         case 0x8c: r = a < b ? a : b; break;
500         default: SET(r); abort();
501         }
502         push(h, r);
503 }
504
505 static void
506 h_unop(Hint *h)
507 {
508         u32int a, r;
509         
510         a = pop(h);
511         switch(h->ip[-1]){
512         case 0x56: r = (ttround(h, a) / 64 & 1) != 0; break;
513         case 0x57: r = (ttround(h, a) / 64 & 1) == 0; break;
514         case 0x5c: r = !a; break;
515         case 0x64: r = (int)a < 0 ? -a : a; break;
516         case 0x65: r = -a; break;
517         case 0x66: r = a & -64; break;
518         case 0x67: r = -(-a & -64); break;
519         case 0x68: case 0x69: case 0x6a: case 0x6b: r = ttround(h, a); break;
520         default: SET(r); abort();
521         }
522         push(h, r);
523 }
524
525 static void
526 h_rs(Hint *h)
527 {
528         int n;
529         
530         n = pop(h);
531         if((uint)n >= h->f->u->maxStorage)
532                 herror(h, "RS[%d] out of bounds");
533         push(h, h->f->storage[n]);
534 }
535
536 static void
537 h_ws(Hint *h)
538 {
539         u32int v;
540         int n;
541         
542         v = pop(h);
543         n = pop(h);
544         if((uint)n >= h->f->u->maxStorage)
545                 herror(h, "WS[%d] out of bounds");
546         h->f->storage[n] = v;
547 }
548
549 static void
550 h_if(Hint *h)
551 {
552         u32int x;
553         
554         x = pop(h);
555         if(!x){
556                 skip(h, 1);
557                 h->ip++;
558         }
559 }
560
561 static void
562 h_else(Hint *h)
563 {
564         skip(h, 1);
565         h->ip++;
566 }
567
568 static void
569 h_nop(Hint *)
570 {
571 }
572
573 static void
574 h_getinfo(Hint *h)
575 {
576         int s;
577         u32int r;
578         
579         s = pop(h);
580         r = 0;
581         if((s & 1) != 0) r |= 3;
582         push(h, r);
583 }
584
585 static void
586 h_scanctrl(Hint *h)
587 {
588         h->f->scanctrl = pop(h);
589 }
590
591 static void
592 h_scantype(Hint *h)
593 {
594         h->f->scantype = pop(h);
595 }
596
597 static void
598 h_roundst(Hint *h)
599 {
600         h->f->rperiod = 64;
601         h->f->rphase = 0;
602         h->f->rthold = 32;
603         switch(h->ip[-1]){
604         case 0x19: /* RTHG */
605                 h->f->rphase = 32;
606                 break;
607         case 0x3D: /* RTDG */
608                 h->f->rperiod = 32;
609                 h->f->rthold = 16;
610                 break;
611         case 0x7C: /* RUTG */
612                 h->f->rthold = 63;
613                 break;
614         case 0x7D: /* RDTG */
615                 h->f->rthold = 0;
616                 break;
617         case 0x7A: /* ROFF */
618                 h->f->rperiod = 0;
619                 break;
620         }
621 }
622
623 static void
624 h_sround(Hint *h)
625 {
626         u8int n;
627         
628         n = pop(h);
629         if((n >> 6 & 3) == 3)
630                 herror(h, "(S)ROUND: period set to reserved value 3");
631         if(h->ip[-1] == 0x77)
632                 h->f->rperiod = 181 >> (2 - (n >> 6 & 3));
633         else
634                 h->f->rperiod = 32 << (n >> 6 & 3);
635         h->f->rphase = h->f->rperiod * (n >> 4 & 3) / 4;
636         if((n & 15) == 0)
637                 h->f->rthold = h->f->rperiod - 1;
638         else
639                 h->f->rthold = h->f->rperiod * ((int)(n & 15) - 4) / 8;
640 }
641
642 static void
643 h_srp(Hint *h)
644 {
645         h->f->rp[h->ip[-1] & 3] = pop(h);
646 }
647
648 static void
649 h_szp(Hint *h)
650 {
651         int n, t;
652         
653         n = pop(h);
654         if(n>>1 != 0) herror(h, "SZP invalid argument %d", n);
655         t = h->ip[-1] - 0x13;
656         if(t == 3) h->f->zp = 7 * n;
657         else h->f->zp = h->f->zp & ~(1<<t) | n<<t;
658 }
659
660 static int
661 project(Hint *h, TTPoint *p, TTPoint *q)
662 {
663         if(q == nil)
664                 return rounddiv(h->f->pvx * p->x + h->f->pvy * p->y, 16384);
665         return rounddiv(h->f->pvx * (p->x - q->x) + h->f->pvy * (p->y - q->y), 16384);
666 }
667
668 static int
669 dualproject(Hint *h, TTPoint *p, TTPoint *q)
670 {
671         if(q == nil)
672                 return rounddiv(h->f->dpvx * p->x + h->f->dpvy * p->y, 16384);
673         return rounddiv(h->f->dpvx * (p->x - q->x) + h->f->dpvy * (p->y - q->y), 16384);
674 }
675
676 static TTPoint
677 forceproject(Hint *h, TTPoint p, int d)
678 {
679         TTFont *f;
680         TTPoint n;
681         int den;
682         vlong k;
683
684         f = h->f;
685         den = f->pvx * f->fvx + f->pvy * f->fvy;
686         if(den == 0) herror(h, "FV and PV orthogonal");
687         k = f->fvx * p.y - f->fvy * p.x;
688         n.x = vrounddiv(16384LL * d * f->fvx - k * f->pvy, den);
689         n.y = vrounddiv(16384LL * d * f->fvy + k * f->pvx, den);
690         n.flags = p.flags;
691         return n;
692 }
693
694 static void
695 h_miap(Hint *h)
696 {
697         int a, pi, di, d, d0, d1;
698         TTPoint p, n;
699         
700         a = h->ip[-1] & 1;
701         di = pop(h);
702         pi = pop(h);
703         if((uint)di >= h->f->ncvt) herror(h, "MIAP out of range");
704         p = getpoint(h, ZP0, pi);
705         d0 = h->f->cvt[di];
706         dprint("cvt %d\n", d0);
707         d1 = project(h, &p, nil);
708         dprint("old %d\n", d1);
709         d = d0;
710         if((h->f->zp & 1) != 0){
711                 if(a && abs(d1 - d) > h->f->cvci)
712                         d = d1;
713         }else{
714                 /* fuck you microsoft */
715                 h->f->twiorg[pi].x = rounddiv(d0 * h->f->pvx, 16384);
716                 h->f->twiorg[pi].y = rounddiv(d0 * h->f->pvy, 16384);
717         }
718         if(a) d = ttround(h, d);
719         n = forceproject(h, p, d);
720         setpoint(h, 0x80, pi, n);
721         h->f->rp[0] = h->f->rp[1] = pi;
722 }
723
724 static void
725 h_mdap(Hint *h)
726 {
727         int pi;
728         TTPoint p;
729         
730         pi = pop(h);
731         p = getpoint(h, ZP0, pi);
732         if((h->ip[-1] & 1) != 0)
733                 p = forceproject(h, p, ttround(h, project(h, &p, nil)));
734         setpoint(h, ZP0, pi, p);
735         h->f->rp[0] = h->f->rp[1] = pi;
736 }
737
738 static void
739 h_ip(Hint *h)
740 {
741         int i;
742         int pi;
743         TTPoint p1, op1, p2, op2, p, op, n;
744         int dp1, dp2, do12, d;
745
746         p1 = getpoint(h, RP1 | ZP0, 0);
747         op1 = getpoint(h, RP1 | ZP0 | ORIG, 0);
748         p2 = getpoint(h, RP2 | ZP1, 0);
749         op2 = getpoint(h, RP2 | ZP1 | ORIG, 0);
750         dp1 = project(h, &p1, nil);
751         dp2 = project(h, &p2, nil);
752         do12 = dualproject(h, &op1, &op2);
753         if(do12 == 0)
754                 herror(h, "invalid IP[] call");
755         for(i = 0; i < h->f->loop; i++){
756                 pi = pop(h);
757                 p = getpoint(h, ZP2, pi);
758                 op = getpoint(h, ZP2 | ORIG, pi);
759                 d = ttfvrounddiv((vlong)dp1 * dualproject(h, &op, &op2) - (vlong)dp2 * dualproject(h, &op, &op1), do12);
760                 n = forceproject(h, p, d);
761                 setpoint(h, 0x82, pi, n);
762                 dprint("(%d,%d) -> (%d,%d)\n", p.x, p.y, n.x, n.y);
763         }
764         h->f->loop = 1;
765 }
766
767 static void
768 h_gc0(Hint *h)
769 {
770         int pi;
771         TTPoint p;
772         
773         pi = pop(h);
774         p = getpoint(h, ZP2, pi);
775         push(h, project(h, &p, nil));
776 }
777
778 static void
779 h_gc1(Hint *h)
780 {
781         int pi;
782         TTPoint p;
783         
784         pi = pop(h);
785         p = getpoint(h, ZP2|ORIG, pi);
786         push(h, dualproject(h, &p, nil));
787 }
788
789 static void
790 h_wcvtp(Hint *h)
791 {
792         u32int v, l;
793         
794         v = pop(h);
795         l = pop(h);
796         if(l >= h->f->ncvt) herror(h, "WCVTP out of range");
797         h->f->cvt[l] = v;
798 }
799
800 static void
801 h_wcvtf(Hint *h)
802 {
803         u32int v, l;
804         
805         v = pop(h);
806         l = pop(h);
807         if(l >= h->f->ncvt) herror(h, "WCVTF out of range");
808         h->f->cvt[l] = rounddiv(v * h->f->ppem * 64, h->f->u->emsize);
809 }
810
811 static void
812 h_rcvt(Hint *h)
813 {
814         u32int l;
815         
816         l = pop(h);
817         if(l >= h->f->ncvt) herror(h, "RCVT out of range");
818         push(h, h->f->cvt[l]);
819 }
820
821 static void
822 h_round(Hint *h)
823 {
824         push(h, ttround(h, pop(h)));
825 }
826
827 static void
828 h_roll(Hint *h)
829 {
830         u32int a, b, c;
831         
832         a = pop(h);
833         b = pop(h);
834         c = pop(h);
835         push(h, b);
836         push(h, a);
837         push(h, c);
838 }
839
840 static void
841 h_pop(Hint *h)
842 {
843         pop(h);
844 }
845
846 static void
847 h_clear(Hint *h)
848 {
849         h->sp = 0;
850 }
851
852 static void
853 h_depth(Hint *h)
854 {
855         push(h, h->sp);
856 }
857
858 static void
859 h_scvtci(Hint *h)
860 {
861         h->f->cvci = pop(h);
862 }
863
864 static void
865 h_mirp(Hint *h)
866 {
867         int a;
868         u32int cvti, pi;
869         TTPoint n, p, p0, op, op0;
870         int d0, d;
871         
872         a = h->ip[-1] & 31;
873         cvti = pop(h);
874         pi = pop(h);
875         if(cvti >= h->f->ncvt)
876                 herror(h, "MIRP out of bounds");
877         d = h->f->cvt[cvti];
878         dprint("cvt %d\n", d);
879         if(abs(d - h->f->singlewval) < h->f->singlewci)
880                 d = d < 0 ? -h->f->singlewci : h->f->singlewci;
881         dprint("single %d\n", d);
882         p = getpoint(h, ZP1, pi);
883         p0 = getpoint(h, ZP0 | RP0, 0);
884         op = getpoint(h, ZP1 | ORIG, pi);
885         op0 = getpoint(h, ZP0 | RP0 | ORIG, 0);
886         d0 = dualproject(h, &op, &op0);
887         if(h->f->autoflip && (d0 ^ d) < 0)
888                 d = -d;
889         if((a & 4) != 0){
890                 if((h->f->zp + 1 & 3) <= 1 && abs(d - d0) > h->f->cvci)
891                         d = d0;
892                 dprint("cutin %d (%d)\n", d, h->f->cvci);
893                 d = ttround(h, d);
894         }
895         dprint("round %d\n", d);
896         if((a & 8) != 0)
897                 if(d0 >= 0){
898                         if(d < h->f->mindist)
899                                 d = h->f->mindist;
900                 }else{
901                         if(d > -h->f->mindist)
902                                 d = -h->f->mindist;
903                 }
904         dprint("mindist %d (%d)\n", d, h->f->mindist);
905         d += project(h, &p0, nil);
906         dprint("total %d\n", d);
907         n = forceproject(h, p, d);
908         setpoint(h, ZP1, pi, n);
909         h->f->rp[1] = h->f->rp[0];
910         h->f->rp[2] = pi;
911         if((a & 16) != 0)
912                 h->f->rp[0] = pi;
913 }
914
915 static void
916 h_msirp(Hint *h)
917 {
918         int a;
919         u32int pi;
920         TTPoint n, p, p0;
921         int d;
922         
923         a = h->ip[-1] & 31;
924         d = pop(h);
925         pi = pop(h);
926         if(abs(d - h->f->singlewval) < h->f->singlewci)
927                 d = d < 0 ? -h->f->singlewci : h->f->singlewci;
928         p = getpoint(h, ZP1, pi);
929         p0 = getpoint(h, ZP0 | RP0, 0);
930         d += project(h, &p0, nil);
931         n = forceproject(h, p, d);
932         setpoint(h, ZP1, pi, n);
933         h->f->rp[1] = h->f->rp[0];
934         h->f->rp[2] = pi;
935         if((a & 1) != 0)
936                 h->f->rp[0] = pi;
937 }
938
939 static void
940 h_deltac(Hint *h)
941 {
942         int n, b, c, arg;
943         
944         n = pop(h);
945         b = (h->ip[-1] - 0x73) * 16 + h->f->deltabase;
946         while(n--){
947                 c = pop(h);
948                 arg = pop(h);
949                 if(h->f->ppem != b + (arg >> 4)) continue;
950                 arg &= 0xf;
951                 arg = arg + (arg >> 3) - 8 << h->f->deltashift;
952                 if((uint)c >= h->f->ncvt) herror(h, "DELTAC argument out of range");
953                 h->f->cvt[c] += arg;
954         }
955 }
956
957 static void
958 h_deltap(Hint *h)
959 {
960         int cnt, b, pi, arg;
961         TTPoint p, n;
962         
963         cnt = pop(h);
964         b = (h->ip[-1] == 0x5d ? 0 : h->ip[-1] - 0x70) * 16 + h->f->deltabase;
965         while(cnt--){
966                 pi = pop(h);
967                 arg = pop(h);
968                 if(h->f->ppem != b + (arg >> 4)) continue;
969                 arg &= 0xf;
970                 arg = arg + (arg >> 3) - 8 << h->f->deltashift;
971                 p = getpoint(h, ZP0, pi);
972                 n = forceproject(h, p, project(h, &p, nil) + arg);
973                 setpoint(h, ZP0, pi, n);
974         }
975 }
976
977 static void
978 h_jmpr(Hint *h)
979 {
980         h->ip += (int)pop(h) - 1;
981         if(h->ip < h->shint || h->ip > h->ehint)
982                 herror(h, "JMPR out of bounds");
983 }
984
985 static void
986 h_jrcond(Hint *h)
987 {
988         u32int e;
989         int n;
990         
991         e = pop(h);
992         n = pop(h) - 1;
993         if((e == 0) == (h->ip[-1] & 1)){
994                 h->ip += n;
995                 if(h->ip < h->shint || h->ip > h->ehint)
996                         herror(h, "JROT/JROF out of bounds");
997         }
998 }
999
1000 static void
1001 h_smd(Hint *h)
1002 {
1003         h->f->mindist = pop(h);
1004 }
1005
1006 static void
1007 h_alignrp(Hint *h)
1008 {
1009         int i, pi;
1010         TTPoint p, q, n;
1011         int dq;
1012         
1013         q = getpoint(h, ZP0 | RP0, 0);
1014         dq = project(h, &q, nil);
1015         for(i = 0; i < h->f->loop; i++){
1016                 pi = pop(h);
1017                 p = getpoint(h, ZP1, pi);
1018                 n = forceproject(h, p, dq);
1019                 setpoint(h, ZP1, pi, n);
1020         }
1021         h->f->loop = 1;
1022 }
1023
1024 static TTPoint
1025 dirvec(TTPoint a, TTPoint b)
1026 {
1027         TTPoint r;
1028         double d;
1029         
1030         r.x = a.x - b.x;
1031         r.y = a.y - b.y;
1032         if(r.x == 0 && r.y == 0) r.x = 1<<14;
1033         else{
1034                 d = hypot(r.x, r.y);
1035                 r.x = r.x / d * 16384;
1036                 r.y = r.y / d * 16384;
1037         }
1038         return r;
1039 }
1040
1041 static void
1042 h_sxvtl(Hint *h)
1043 {
1044         int pi1, pi2;
1045         TTPoint p1, p2;
1046         TTPoint p;
1047         int z;
1048         
1049         pi2 = pop(h);
1050         pi1 = pop(h);
1051         p1 = getpoint(h, ZP1, pi1);
1052         p2 = getpoint(h, ZP2, pi2);
1053         p = dirvec(p1, p2);
1054         if((h->ip[-1] & 1) != 0){
1055                 z = p.x;
1056                 p.x = -p.y;
1057                 p.y = z;
1058         }
1059         if(h->ip[-1] >= 8){
1060                 h->f->fvx = p.x;
1061                 h->f->fvy = p.y;
1062         }else{
1063                 h->f->dpvx = h->f->pvx = p.x;
1064                 h->f->dpvy = h->f->pvy = p.y;
1065         }
1066 }
1067
1068 static void
1069 h_sfvfs(Hint *h)
1070 {
1071         h->f->fvy = pop(h);
1072         h->f->fvx = pop(h);
1073 }
1074
1075 static void
1076 h_spvfs(Hint *h)
1077 {
1078         h->f->dpvy = h->f->pvy = pop(h);
1079         h->f->dpvx = h->f->pvx = pop(h);
1080 }
1081
1082 static void
1083 h_gfv(Hint *h)
1084 {
1085         push(h, h->f->fvx);
1086         push(h, h->f->fvy);
1087 }
1088
1089 static void
1090 h_gpv(Hint *h)
1091 {
1092         push(h, h->f->pvx);
1093         push(h, h->f->pvy);
1094 }
1095
1096 static void
1097 h_mdrp(Hint *h)
1098 {
1099         int pi;
1100         TTPoint p, p0, op, op0, n;
1101         int d, d0;
1102         
1103         pi = pop(h);
1104         p = getpoint(h, ZP1, pi);
1105         p0 = getpoint(h, ZP0 | RP0, 0);
1106         op = getpoint(h, ZP1 | ORIG, pi);
1107         op0 = getpoint(h, ZP0 | RP0 | ORIG, 0);
1108         d = d0 = dualproject(h, &op, &op0);
1109         if(abs(d - h->f->singlewval) < h->f->singlewci)
1110                 d = d >= 0 ? -h->f->singlewci : h->f->singlewci;
1111         if((h->ip[-1] & 4) != 0)
1112                 d = ttround(h, d);
1113         if((h->ip[-1] & 8) != 0)
1114                 if(d0 >= 0){
1115                         if(d < h->f->mindist)
1116                                 d = h->f->mindist;
1117                 }else{
1118                         if(d > -h->f->mindist)
1119                                 d = -h->f->mindist;
1120                 }
1121         n = forceproject(h, p, d + project(h, &p0, nil));
1122         setpoint(h, ZP1, pi, n);
1123         h->f->rp[1] = h->f->rp[0];
1124         h->f->rp[2] = pi;
1125         if((h->ip[-1] & 16) != 0)
1126                 h->f->rp[0] = pi;
1127 }
1128
1129 static void
1130 h_sdpvtl(Hint *h)
1131 {
1132         int pi1, pi2;
1133         TTPoint p1, p2;
1134         TTPoint op1, op2;
1135         TTPoint p;
1136         
1137         pi2 = pop(h);
1138         pi1 = pop(h);
1139         p1 = getpoint(h, ZP1, pi1);
1140         p2 = getpoint(h, ZP2, pi2);
1141         op1 = getpoint(h, ZP1 | ORIG, pi1);
1142         op2 = getpoint(h, ZP2 | ORIG, pi2);
1143         p = dirvec(p1, p2);
1144         if((h->ip[-1] & 1) != 0){
1145                 h->f->pvx = -p.y;
1146                 h->f->pvy = p.x;
1147         }else{
1148                 h->f->pvx = p.x;
1149                 h->f->pvy = p.y;
1150         }
1151         p = dirvec(op1, op2);
1152         if((h->ip[-1] & 1) != 0){
1153                 h->f->dpvx = -p.y;
1154                 h->f->dpvy = p.x;
1155         }else{
1156                 h->f->dpvx = p.x;
1157                 h->f->dpvy = p.y;
1158         }
1159 }
1160
1161 static void
1162 h_sfvtpv(Hint *h)
1163 {
1164         h->f->fvx = h->f->pvx;
1165         h->f->fvy = h->f->pvy;
1166 }
1167
1168 static void
1169 h_sdb(Hint *h)
1170 {
1171         h->f->deltabase = pop(h);
1172 }
1173
1174 static void
1175 h_sds(Hint *h)
1176 {
1177         h->f->deltashift = pop(h);
1178 }
1179
1180 static void
1181 h_ssw(Hint *h)
1182 {
1183         h->f->singlewval = pop(h);
1184 }
1185
1186 static void
1187 h_sswci(Hint *h)
1188 {
1189         h->f->singlewci = pop(h);
1190 }
1191
1192 static void
1193 h_fliponoff(Hint *h)
1194 {
1195         h->f->autoflip = h->ip[-1] & 1;
1196 }
1197
1198 static void
1199 h_md0(Hint *h)
1200 {
1201         TTPoint p0, p1;
1202         
1203         p1 = getpoint(h, ZP1, pop(h));
1204         p0 = getpoint(h, ZP0, pop(h));
1205         push(h, project(h, &p0, &p1));
1206 }
1207
1208 static void
1209 h_md1(Hint *h)
1210 {
1211         TTPoint p0, p1;
1212         
1213         p1 = getpoint(h, ZP1 | ORIG, pop(h));
1214         p0 = getpoint(h, ZP0 | ORIG, pop(h));
1215         push(h, dualproject(h, &p0, &p1));
1216 }
1217
1218 static void
1219 h_shpix(Hint *h)
1220 {
1221         int i, d, pi, dx, dy;
1222         TTPoint p;
1223         
1224         d = pop(h);
1225         dx = vrounddiv((vlong)h->f->fvx * d, 16384);
1226         dy = vrounddiv((vlong)h->f->fvy * d, 16384);
1227         for(i = 0; i < h->f->loop; i++){
1228                 pi = pop(h);
1229                 p = getpoint(h, ZP2, pi);
1230                 p.x += dx;
1231                 p.y += dy;
1232                 setpoint(h, ZP2, pi, p);
1233         }
1234         h->f->loop = 1;
1235 }
1236
1237 static void
1238 iup1(Hint *h, int ip, int iq, int i, int e)
1239 {
1240         TTGlyph *g;
1241         int z;
1242         
1243         g = h->g;
1244         if(g->ptorg[ip].x == g->ptorg[iq].x)
1245                 for(; i <= e; i++)
1246                         g->pt[i].x = g->ptorg[i].x + g->pt[iq].x - g->ptorg[iq].x;
1247         else
1248                 for(; i <= e; i++){
1249                         z = (g->ptorg[i].x - g->ptorg[iq].x) * 64 / (g->ptorg[ip].x - g->ptorg[iq].x);
1250                         if(z < 0) z = 0;
1251                         else if(z > 64) z = 64;
1252                         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);
1253                 }
1254 }
1255
1256 static void
1257 iup0(Hint *h, int ip, int iq, int i, int e)
1258 {
1259         TTGlyph *g;
1260         int z;
1261         
1262         g = h->g;
1263         if(g->ptorg[ip].y == g->ptorg[iq].y)
1264                 for(; i <= e; i++)
1265                         g->pt[i].y = g->ptorg[i].y + g->pt[iq].y - g->ptorg[iq].y;
1266         else
1267                 for(; i <= e; i++){
1268                         z = (g->ptorg[i].y - g->ptorg[iq].y) * 64 / (g->ptorg[ip].y - g->ptorg[iq].y);
1269                         if(z < 0) z = 0;
1270                         else if(z > 64) z = 64;
1271                         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);
1272                 }
1273 }
1274
1275 static void
1276 h_iup(Hint *h)
1277 {
1278         int i, j, t0, t1;
1279         TTPoint *p;
1280         void (*iupp)(Hint *, int, int, int, int);
1281
1282         iupp = (h->ip[-1] & 1) != 0 ? iup1 : iup0;
1283         for(i = 0; i < h->g->ncon; i++){
1284                 t0 = t1 = -1;
1285                 for(j = h->g->confst[i]; j < h->g->confst[i+1]; j++){
1286                         p = &h->g->pt[j];
1287                         if((p->flags & TOUCHY>>(h->ip[-1]&1)) != 0){
1288                                 if(t0 < 0)
1289                                         t0 = j;
1290                                 if(t1 >= 0)
1291                                         iupp(h, t1, j, t1 + 1, j - 1);
1292                                 t1 = j;
1293                         }
1294                 }
1295                 if(t1 != t0){
1296                         iupp(h, t1, t0, h->g->confst[i], t0 - 1);
1297                         iupp(h, t1, t0, t1 + 1, h->g->confst[i+1]-1);
1298                 }else if(t0 >= 0)
1299                         iupp(h, t0, t0, h->g->confst[i], h->g->confst[i+1]-1);
1300         }
1301         
1302         for(i = 0; i < h->g->npt; i++)
1303                 dprint("%d: %+π\n", i, h->g->pt[i]);
1304 }
1305
1306 static void
1307 h_sloop(Hint *h)
1308 {
1309         int n;
1310         
1311         n = pop(h);
1312         if(n <= 0)
1313                 herror(h, "SLOOP invalid argument %d", n);
1314         h->f->loop = n;
1315 }
1316
1317 static void
1318 h_scfs(Hint *h)
1319 {
1320         int d, pi;
1321         TTPoint p, n;
1322         
1323         d = pop(h);
1324         pi = pop(h);
1325         p = getpoint(h, ZP2, pi);
1326         n = forceproject(h, p, d);
1327         setpoint(h, ZP2, pi, n);
1328 }
1329
1330 static void
1331 h_fliprg(Hint *h)
1332 {
1333         int i, e;
1334         
1335         e = pop(h);
1336         i = pop(h);
1337         if(h->g == nil)
1338                 herror(h, "FLIPRG without glyph");
1339         for(; i <= e; i++)
1340                 if((int)i < h->g->npt)
1341                         h->g->pt[i].flags = h->g->pt[i].flags & ~1 | h->ip[-1] & 1;
1342 }
1343
1344 static void
1345 h_isect(Hint *h)
1346 {
1347         int a0i, a1i, b0i, b1i, pi;
1348         TTPoint a0, a1, b0, b1, p;
1349         int n0x, n0y;
1350         vlong n0c;
1351         int n1x, n1y;
1352         vlong n1c;
1353         int Δ;
1354         
1355         a0i = pop(h);
1356         a1i = pop(h);
1357         b0i = pop(h);
1358         b1i = pop(h);
1359         pi = pop(h);
1360         a0 = getpoint(h, ZP0, a0i);
1361         a1 = getpoint(h, ZP0, a1i);
1362         b0 = getpoint(h, ZP1, b0i);
1363         b1 = getpoint(h, ZP1, b1i);
1364         p = getpoint(h, ZP2, pi);
1365         n0x = a1.y - a0.y;
1366         n0y = a0.x - a1.x;
1367         n0c = (vlong)n0x * a0.x + (vlong)n0y * a0.y;
1368         n1x = b1.y - b0.y;
1369         n1y = b0.x - b1.x;
1370         n1c = (vlong)n1x * b0.x + (vlong)n1y * b0.y;
1371         Δ = (vlong)n1x * n0y - (vlong)n0x * n1y;
1372         if(Δ == 0){
1373                 p.x = ((a0.x + a1.x) / 2 + (b0.x + b1.x) / 2) / 2;
1374                 p.y = ((a0.y + a1.y) / 2 + (b0.y + b1.y) / 2) / 2;
1375         }else{
1376                 p.x = vrounddiv(n0y * n1c - n1y * n0c, Δ);
1377                 p.y = vrounddiv(n1x * n0c - n0x * n1c, Δ);
1378         }
1379         p.flags |= TOUCH;
1380         setpoint(h, ZP2, pi, p);
1381 }
1382
1383 static void
1384 h_shp(Hint *h)
1385 {
1386         int i;
1387         TTPoint rp, orp;
1388         int pi;
1389         TTPoint p, n;
1390         int d, dp;
1391
1392         if((h->ip[-1] & 1) != 0){
1393                 rp = getpoint(h, RP1|ZP0, 0);
1394                 orp = getpoint(h, RP1|ZP0|ORIG, 0);
1395         }else{
1396                 rp = getpoint(h, RP2|ZP1, 0);
1397                 orp = getpoint(h, RP2|ZP1|ORIG, 0);
1398         }
1399         
1400         d = project(h, &rp, &orp);
1401         for(i = 0; i < h->f->loop; i++){
1402                 pi = pop(h);
1403                 p = getpoint(h, ZP2, pi);
1404                 dp = project(h, &p, nil);
1405                 n = forceproject(h, p, dp + d);
1406                 setpoint(h, ZP2, pi, n);
1407         }
1408         h->f->loop = 1;
1409 }
1410
1411 static void
1412 h_shc(Hint *h)
1413 {
1414         int i, c;
1415         int rpi;
1416         TTPoint rp, orp;
1417         TTPoint p, n;
1418         int d, dp;
1419
1420         if((h->ip[-1] & 1) != 0){
1421                 rpi = h->f->rp[1];
1422                 if(((h->f->zp ^ h->f->zp >> 2) & 1) != 0)
1423                         rpi = -1;
1424                 rp = getpoint(h, RP1|ZP0, 0);
1425                 orp = getpoint(h, RP1|ZP0|ORIG, 0);
1426         }else{
1427                 rpi = h->f->rp[2];
1428                 if(((h->f->zp ^ h->f->zp >> 1) & 1) != 0)
1429                         rpi = -1;
1430                 rp = getpoint(h, RP2|ZP1, 0);
1431                 orp = getpoint(h, RP2|ZP1|ORIG, 0);
1432         }
1433         c = pop(h);
1434         if(h->g == nil)
1435                 herror(h, "SHC[] outside of glyf program");
1436         if((uint)c >= h->g->ncon)
1437                 herror(h, "contour %d out of range", c);
1438         d = project(h, &rp, &orp);
1439         for(i = h->g->confst[c]; i < h->g->confst[c+1]; i++){
1440                 if(i == rpi) continue;
1441                 p = getpoint(h, ZP2, i);
1442                 dp = project(h, &p, nil);
1443                 n = forceproject(h, p, dp + d);
1444                 setpoint(h, ZP2, i, n);
1445         }
1446         h->f->loop = 1;
1447 }
1448
1449 static void (*itable[256])(Hint *) = {
1450         [0x00] h_svtca, h_svtca, h_svtca, h_svtca, h_svtca, h_svtca,
1451         [0x06] h_sxvtl, h_sxvtl, h_sxvtl, h_sxvtl,
1452         [0x0a] h_spvfs,
1453         [0x0b] h_sfvfs,
1454         [0x0c] h_gpv,
1455         [0x0d] h_gfv,
1456         [0x0e] h_sfvtpv,
1457         [0x0f] h_isect,
1458         [0x10] h_srp, h_srp, h_srp,
1459         [0x13] h_szp, h_szp, h_szp, h_szp,
1460         [0x17] h_sloop,
1461         [0x18] h_roundst, h_roundst,
1462         [0x1a] h_smd,
1463         [0x1b] h_else,
1464         [0x1c] h_jmpr,
1465         [0x1d] h_scvtci,
1466         [0x1e] h_sswci,
1467         [0x1f] h_ssw,
1468         [0x20] h_dup,
1469         [0x21] h_pop,
1470         [0x22] h_clear,
1471         [0x23] h_swap,
1472         [0x24] h_depth,
1473         [0x25] h_cindex,
1474         [0x26] h_mindex,
1475         [0x2a] h_loopcall,
1476         [0x2b] h_call,
1477         [0x2c] h_fdef,
1478         [0x2e] h_mdap, h_mdap,
1479         [0x30] h_iup, h_iup,
1480         [0x32] h_shp, h_shp,
1481         [0x34] h_shc, h_shc,
1482         [0x38] h_shpix,
1483         [0x39] h_ip,
1484         [0x3a] h_msirp, h_msirp,
1485         [0x3c] h_alignrp,
1486         [0x3d] h_roundst,
1487         [0x3e] h_miap, h_miap,
1488         [0x40] h_npushb,
1489         [0x41] h_npushw,
1490         [0x42] h_ws,
1491         [0x43] h_rs,
1492         [0x44] h_wcvtp,
1493         [0x45] h_rcvt,
1494         [0x46] h_gc0, h_gc1,
1495         [0x48] h_scfs,
1496         [0x49] h_md0, h_md1,
1497         [0x4b] h_mppem,
1498         [0x4d] h_fliponoff, h_fliponoff,
1499         [0x4f] h_nop,
1500         [0x50] h_binop, h_binop, h_binop, h_binop, h_binop, h_binop,
1501         [0x56] h_unop, h_unop,
1502         [0x58] h_if,
1503         [0x59] h_nop, /* endif */
1504         [0x5a] h_binop, h_binop,
1505         [0x5c] h_unop,
1506         [0x5d] h_deltap,
1507         [0x5e] h_sdb,
1508         [0x5f] h_sds,
1509         [0x60] h_binop, h_binop, h_binop, h_binop, h_unop, h_unop, h_unop, h_unop,
1510         [0x68] h_unop, h_unop, h_unop, h_unop, h_nop, h_nop, h_nop, h_nop,
1511         [0x70] h_wcvtf,
1512         [0x71] h_deltap, h_deltap,
1513         [0x73] h_deltac, h_deltac, h_deltac,
1514         [0x76] h_sround, h_sround,
1515         [0x78] h_jrcond, h_jrcond,
1516         [0x7a] h_roundst,
1517         [0x7c] h_roundst, h_roundst,
1518         [0x7e] h_pop,
1519         [0x7f] h_pop,
1520         [0x81] h_fliprg, h_fliprg,
1521         [0x85] h_scanctrl,
1522         [0x86] h_sdpvtl, h_sdpvtl,
1523         [0x88] h_getinfo,
1524         [0x8a] h_roll,
1525         [0x8b] h_binop, h_binop,
1526         [0x8d] h_scantype,
1527         [0x8e] h_instctrl,
1528         [0xb0] h_pushb, h_pushb, h_pushb, h_pushb,
1529                h_pushb, h_pushb, h_pushb, h_pushb,
1530         [0xb8] h_pushw, h_pushw, h_pushw, h_pushw,
1531                h_pushw, h_pushw, h_pushw, h_pushw,
1532         [0xc0] h_mdrp, h_mdrp, h_mdrp, h_mdrp, h_mdrp, h_mdrp, h_mdrp, h_mdrp,
1533                h_mdrp, h_mdrp, h_mdrp, h_mdrp, h_mdrp, h_mdrp, h_mdrp, h_mdrp,
1534                h_mdrp, h_mdrp, h_mdrp, h_mdrp, h_mdrp, h_mdrp, h_mdrp, h_mdrp,
1535                h_mdrp, h_mdrp, h_mdrp, h_mdrp, h_mdrp, h_mdrp, h_mdrp, h_mdrp,
1536         [0xe0] h_mirp, h_mirp, h_mirp, h_mirp, h_mirp, h_mirp, h_mirp, h_mirp,
1537                h_mirp, h_mirp, h_mirp, h_mirp, h_mirp, h_mirp, h_mirp, h_mirp,
1538                h_mirp, h_mirp, h_mirp, h_mirp, h_mirp, h_mirp, h_mirp, h_mirp,
1539                h_mirp, h_mirp, h_mirp, h_mirp, h_mirp, h_mirp, h_mirp, h_mirp,
1540 };
1541
1542 static int
1543 pointfmt(Fmt *f)
1544 {
1545         TTPoint p;
1546         
1547         p = va_arg(f->args, TTPoint);
1548         if((f->flags & FmtSign) != 0)
1549                 return fmtprint(f, "(%.2f,%.2f,%d)", (float)p.x/64, (float)p.y/64, p.flags);
1550         else
1551                 return fmtprint(f, "(%d,%d,%d)", p.x, p.y, p.flags);
1552 }
1553
1554 static void
1555 run(Hint *h)
1556 {
1557         while(h->ip < h->ehint){
1558                 if(debug) debugprint(h, 0);
1559                 if(itable[*h->ip] == nil)
1560                         sysfatal("unknown hint instruction %#.2x", *h->ip);
1561                 else
1562                         itable[*h->ip++](h);
1563         }
1564 }
1565
1566 static int
1567 runpg(TTFont *f, TTGlyph *g, uchar *buf, int n)
1568 {
1569         Hint h;
1570         static int didfmt;
1571
1572         if(debug && !didfmt){
1573                 fmtinstall(L'π', pointfmt);
1574                 didfmt = 1;
1575         }
1576         memset(&h, 0, sizeof(Hint));
1577         if(setjmp(h.jmp) != 0){
1578                 errstr(h.err, sizeof(h.err));
1579                 return -1;
1580         }
1581         h.g = g;
1582         h.f = f;
1583         h.stack = f->hintstack;
1584         h.nstack = f->u->maxStackElements;
1585         h.ip = h.shint = buf;
1586         h.ehint = buf + n;
1587         run(&h);
1588         return 0;
1589 }
1590
1591 int
1592 ttfhint(TTGlyph *g)
1593 {
1594         int rc, i;
1595
1596         if((g->font->defstate.instctrl & 1<<1) != 0)
1597                 return 0;
1598         dprint("HINT:\n");
1599         if((g->font->defstate.instctrl & 1<<2) != 0)
1600                 g->font->TTGState = defstate;
1601         else
1602                 g->font->TTGState = g->font->defstate;
1603         rc = runpg(g->font, g, g->hint, g->nhint);
1604         if(debug && rc >= 0){
1605                 for(i = 0; i < g->npt; i++)
1606                         dprint("%d: %+π\n", i, g->pt[i]);
1607         }
1608         return rc;
1609 }
1610
1611 int
1612 ttfrunfpgm(TTFont *f)
1613 {
1614         int len, rc;
1615         u8int *buf;
1616
1617         f->TTGState = defstate;
1618         f->defstate = defstate;
1619         len = ttfgototable(f->u, "fpgm");
1620         if(len <= 0)
1621                 return 0;
1622         buf = mallocz(len, 1);
1623         if(buf == nil)
1624                 return -1;
1625         Bread(f->u->bin, buf, len);
1626         dprint("FPGM:\n");
1627         rc = runpg(f, nil, buf, len);
1628         free(buf);
1629         return rc;
1630 }
1631
1632 int
1633 ttfruncvt(TTFont *f)
1634 {
1635         int len, rc;
1636         u8int *buf;
1637
1638         f->TTGState = defstate;
1639         f->defstate = defstate;
1640         len = ttfgototable(f->u, "prep");
1641         if(len <= 0)
1642                 return 0;
1643         buf = mallocz(len, 1);
1644         if(buf == nil)
1645                 return -1;
1646         Bread(f->u->bin, buf, len);
1647         dprint("CVT:\n");
1648         rc = runpg(f, nil, buf, len);
1649         free(buf);
1650         if(rc >= 0){
1651                 f->zp = 7;
1652                 f->rp[0] = 0;
1653                 f->rp[1] = 0;
1654                 f->rp[2] = 0;
1655                 f->loop = 1;
1656                 f->rperiod = 64;
1657                 f->rphase = 0;
1658                 f->rthold = 32;
1659                 f->fvx = 16384;
1660                 f->fvy = 0;
1661                 f->pvx = 16384;
1662                 f->pvy = 0;
1663                 f->dpvx = 16384;
1664                 f->dpvy = 0;
1665                 f->defstate = f->TTGState;
1666         }
1667         return rc;
1668 }