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