1 /*
2  * Copyright 2014 Adobe Systems Incorporated (http://www.adobe.com/).
3  * All Rights Reserved.
4  *
5  * This software is licensed as OpenSource, under the Apache License, Version
6  * 2.0.
7  * This license is available at: http://opensource.org/licenses/Apache-2.0.
8  */
9 
10 #include <math.h>
11 
12 #include "ac.h"
13 
14 #define WRTABS_COMMENT (0)
15 
16 static Fixed currentx, currenty;
17 static bool firstFlex, wrtHintInfo;
18 #define MAXBUFFLEN 127
19 static char S0[MAXBUFFLEN + 1];
20 static HintPoint* bst;
21 static char bch;
22 static Fixed bx, by;
23 static bool bstB;
24 
25 int32_t
FRnd(int32_t x)26 FRnd(int32_t x)
27 {
28     /* This is meant to work on Fixed 24.8 values, not the elt path (x,y) which
29      * are 25.7 */
30     int32_t r;
31     r = x;
32     if (gRoundToInt) {
33         r = r + (1 << 7);
34         r = r & ~0xFF;
35     }
36     return r;
37 }
38 
39 #define WriteString(...) ACBufferWriteF(gBezOutput, __VA_ARGS__)
40 
41 /* Note: The 8 bit fixed fraction cannot support more than 2 decimal places. */
42 #define WRTNUM(i) WriteString("%d ", (int32_t)(i))
43 #define WRTRNUM(i) WriteString("%0.2f ", round((double)(i)*100) / 100)
44 
45 static void
wrtxa(Fixed x)46 wrtxa(Fixed x)
47 {
48     if ((gRoundToInt) || (FracPart(x) == 0)) {
49         Fixed i = FRnd(x);
50         WRTNUM(FTrunc(i));
51         currentx = i;
52     } else {
53         float r;
54         currentx = x;
55         r = (float)FIXED2FLOAT(x);
56         WRTRNUM(r);
57     }
58 }
59 
60 static void
wrtya(Fixed y)61 wrtya(Fixed y)
62 {
63     if ((gRoundToInt) || (FracPart(y) == 0)) {
64         Fixed i = FRnd(y);
65         WRTNUM(FTrunc(i));
66         currenty = i;
67     } else {
68         float r;
69         currenty = y;
70         r = (float)FIXED2FLOAT(y);
71         WRTRNUM(r);
72     }
73 }
74 
75 #define wrtcda(c)                                                              \
76     wrtxa(c.x);                                                                \
77     wrtya(c.y)
78 
79 /*To avoid pointless hint subs*/
80 #define HINTMAXSTR 2048
81 static char hintmaskstr[HINTMAXSTR];
82 static char prevhintmaskstr[HINTMAXSTR];
83 
84 static void
safestrcat(char * s1,char * s2)85 safestrcat(char* s1, char* s2)
86 {
87     if (strlen(s1) + strlen(s2) + 1 > HINTMAXSTR) {
88         LogMsg(LOGERROR, FATALERROR, "Hint information overflowing buffer.");
89     } else {
90         strcat(s1, s2);
91     }
92 }
93 
94 #define sws(str) safestrcat(hintmaskstr, (char*)str)
95 
96 #define SWRTNUM(i)                                                             \
97     {                                                                          \
98         snprintf(S0, MAXBUFFLEN, "%d ", (int32_t)(i));                         \
99         sws(S0);                                                               \
100     }
101 
102 #define SWRTNUMA(i)                                                            \
103     {                                                                          \
104         snprintf(S0, MAXBUFFLEN, "%0.2f ", round((double)(i)*100) / 100);      \
105         sws(S0);                                                               \
106     }
107 
108 static void
NewBest(HintPoint * lst)109 NewBest(HintPoint* lst)
110 {
111     bst = lst;
112     bch = lst->c;
113     if (bch == 'y' || bch == 'm') {
114         Fixed x0, x1;
115         bstB = true;
116         x0 = lst->x0;
117         x1 = lst->x1;
118         bx = NUMMIN(x0, x1);
119     } else {
120         Fixed y0, y1;
121         bstB = false;
122         y0 = lst->y0;
123         y1 = lst->y1;
124         by = NUMMIN(y0, y1);
125     }
126 }
127 
128 static void
WriteOne(Fixed s)129 WriteOne(Fixed s)
130 { /* write s to output file */
131     if (FracPart(s) == 0) {
132         SWRTNUM(FTrunc(s))
133     } else {
134         float d = (float)FIXED2FLOAT(s);
135         SWRTNUMA(d);
136     }
137 }
138 
139 static void
WritePointItem(HintPoint * lst)140 WritePointItem(HintPoint* lst)
141 {
142     switch (lst->c) {
143         case 'b':
144         case 'v':
145             WriteOne(lst->y0);
146             WriteOne(lst->y1 - lst->y0);
147             sws(((lst->c == 'b') ? "rb" : "rv"));
148             break;
149         case 'y':
150         case 'm':
151             WriteOne(lst->x0);
152             WriteOne(lst->x1 - lst->x0);
153             sws(((lst->c == 'y') ? "ry" : "rm"));
154             break;
155         default: {
156             LogMsg(LOGERROR, NONFATALERROR, "Illegal point list data.");
157         }
158     }
159     sws(" % ");
160     SWRTNUM(lst->p0 != NULL ? lst->p0->count : 0);
161     SWRTNUM(lst->p1 != NULL ? lst->p1->count : 0);
162     sws("\n");
163 }
164 
165 static void
WrtPntLst(HintPoint * lst)166 WrtPntLst(HintPoint* lst)
167 {
168     HintPoint* ptLst;
169     char ch;
170     Fixed x0, x1, y0, y1;
171     ptLst = lst;
172 
173     while (lst != NULL) { /* mark all as not yet done */
174         lst->done = false;
175         lst = lst->next;
176     }
177     while (true) { /* write in sort order */
178         lst = ptLst;
179         bst = NULL;
180         while (lst != NULL) { /* find first not yet done as init best */
181             if (!lst->done) {
182                 NewBest(lst);
183                 break;
184             }
185             lst = lst->next;
186         }
187         if (bst == NULL) {
188             break; /* finished with entire list */
189         }
190         lst = bst->next;
191         while (lst != NULL) { /* search for best */
192             if (!lst->done) {
193                 ch = lst->c;
194                 if (ch > bch) {
195                     NewBest(lst);
196                 } else if (ch == bch) {
197                     if (bstB) {
198                         x0 = lst->x0;
199                         x1 = lst->x1;
200                         if (NUMMIN(x0, x1) < bx) {
201                             NewBest(lst);
202                         }
203                     } else {
204                         y0 = lst->y0;
205                         y1 = lst->y1;
206                         if (NUMMIN(y0, y1) < by) {
207                             NewBest(lst);
208                         }
209                     }
210                 }
211             }
212             lst = lst->next;
213         }
214         bst->done = true; /* mark as having been done */
215         WritePointItem(bst);
216     }
217 }
218 
219 static void
wrtnewhints(PathElt * e)220 wrtnewhints(PathElt* e)
221 {
222     if (!wrtHintInfo) {
223         return;
224     }
225     hintmaskstr[0] = '\0';
226     WrtPntLst(gPtLstArray[e->newhints]);
227     if (strcmp(prevhintmaskstr, hintmaskstr)) {
228         WriteString("beginsubr snc\n%sendsubr enc\nnewcolors\n", hintmaskstr);
229         strcpy(prevhintmaskstr, hintmaskstr);
230     }
231 }
232 
233 static bool
IsFlex(PathElt * e)234 IsFlex(PathElt* e)
235 {
236     PathElt *e0, *e1;
237     if (firstFlex) {
238         e0 = e;
239         e1 = e->next;
240     } else {
241         e0 = e->prev;
242         e1 = e;
243     }
244     return (e0 != NULL && e0->isFlex && e1 != NULL && e1->isFlex);
245 }
246 
247 static void
mt(Cd c,PathElt * e)248 mt(Cd c, PathElt* e)
249 {
250     if (e->newhints != 0) {
251         wrtnewhints(e);
252     }
253     wrtcda(c);
254     WriteString("mt\n");
255 }
256 
257 static void
dt(Cd c,PathElt * e)258 dt(Cd c, PathElt* e)
259 {
260     if (e->newhints != 0) {
261         wrtnewhints(e);
262     }
263     wrtcda(c);
264     WriteString("dt\n");
265 }
266 
267 static Fixed flX, flY;
268 static Cd fc1, fc2, fc3;
269 
270 #define wrtpreflx2a(c)                                                         \
271     wrtcda(c);                                                                 \
272     WriteString("rmt\npreflx2a\n")
273 
274 static void
wrtflex(Cd c1,Cd c2,Cd c3,PathElt * e)275 wrtflex(Cd c1, Cd c2, Cd c3, PathElt* e)
276 {
277     int32_t dmin, delta;
278     bool yflag;
279     Cd c13;
280     float shrink, r1, r2;
281     if (firstFlex) {
282         flX = currentx;
283         flY = currenty;
284         fc1 = c1;
285         fc2 = c2;
286         fc3 = c3;
287         firstFlex = false;
288         return;
289     }
290     yflag = e->yFlex;
291     dmin = gDMin;
292     delta = gDelta;
293     WriteString("preflx1\n");
294     if (yflag) {
295         if (fc3.y == c3.y) {
296             c13.y = c3.y;
297         } else {
298             acfixtopflt(fc3.y - c3.y, &shrink);
299             shrink = (float)delta / shrink;
300             if (shrink < 0.0f) {
301                 shrink = -shrink;
302             }
303             acfixtopflt(fc3.y - c3.y, &r1);
304             r1 *= shrink;
305             acfixtopflt(c3.y, &r2);
306             r1 += r2;
307             c13.y = acpflttofix(&r1);
308         }
309         c13.x = fc3.x;
310     } else {
311         if (fc3.x == c3.x) {
312             c13.x = c3.x;
313         } else {
314             acfixtopflt(fc3.x - c3.x, &shrink);
315             shrink = (float)delta / shrink;
316             if (shrink < 0.0f) {
317                 shrink = -shrink;
318             }
319             acfixtopflt(fc3.x - c3.x, &r1);
320             r1 *= shrink;
321             acfixtopflt(c3.x, &r2);
322             r1 += r2;
323             c13.x = acpflttofix(&r1);
324         }
325         c13.y = fc3.y;
326     }
327 
328     wrtpreflx2a(c13);
329     wrtpreflx2a(fc1);
330     wrtpreflx2a(fc2);
331     wrtpreflx2a(fc3);
332     wrtpreflx2a(c1);
333     wrtpreflx2a(c2);
334     wrtpreflx2a(c3);
335     currentx = flX;
336     currenty = flY;
337     wrtcda(fc1);
338     wrtcda(fc2);
339     wrtcda(fc3);
340     wrtcda(c1);
341     wrtcda(c2);
342     wrtcda(c3);
343     WRTNUM(dmin);
344     WRTNUM(delta);
345     WRTNUM(yflag);
346     WRTNUM(FTrunc(FRnd(currentx)));
347     WRTNUM(FTrunc(FRnd(currenty)));
348     WriteString("flxa\n");
349     firstFlex = true;
350 }
351 
352 static void
ct(Cd c1,Cd c2,Cd c3,PathElt * e)353 ct(Cd c1, Cd c2, Cd c3, PathElt* e)
354 {
355     if (e->newhints != 0) {
356         wrtnewhints(e);
357     }
358     if (e->isFlex && IsFlex(e)) {
359         wrtflex(c1, c2, c3, e);
360     } else {
361         wrtcda(c1);
362         wrtcda(c2);
363         wrtcda(c3);
364         WriteString("ct\n");
365     }
366 }
367 
368 static void
cp(PathElt * e)369 cp(PathElt* e)
370 {
371     if (e->newhints != 0) {
372         wrtnewhints(e);
373     }
374     WriteString("cp\n");
375 }
376 
377 static void
NumberPath(void)378 NumberPath(void)
379 {
380     int16_t cnt;
381     PathElt* e;
382     e = gPathStart;
383     cnt = 1;
384     while (e != NULL) {
385         e->count = cnt++;
386         e = e->next;
387     }
388 }
389 
390 void
SaveFile(void)391 SaveFile(void)
392 {
393     PathElt* e = gPathStart;
394     Cd c1, c2, c3;
395 
396     WriteString("%% %s\n", gGlyphName);
397     wrtHintInfo = (gPathStart != NULL && gPathStart != gPathEnd);
398     NumberPath();
399     prevhintmaskstr[0] = '\0';
400     if (wrtHintInfo && (!e->newhints)) {
401         hintmaskstr[0] = '\0';
402         WrtPntLst(gPtLstArray[0]);
403         WriteString("%s", hintmaskstr);
404         strcpy(prevhintmaskstr, hintmaskstr);
405     }
406 
407     WriteString("sc\n");
408     firstFlex = true;
409     currentx = currenty = 0;
410     while (e != NULL) {
411         switch (e->type) {
412             case CURVETO:
413                 c1.x = e->x1;
414                 c1.y = -e->y1;
415                 c2.x = e->x2;
416                 c2.y = -e->y2;
417                 c3.x = e->x3;
418                 c3.y = -e->y3;
419                 ct(c1, c2, c3, e);
420                 break;
421             case LINETO:
422                 c1.x = e->x;
423                 c1.y = -e->y;
424                 dt(c1, e);
425                 break;
426             case MOVETO:
427                 c1.x = e->x;
428                 c1.y = -e->y;
429                 mt(c1, e);
430                 break;
431             case CLOSEPATH:
432                 cp(e);
433                 break;
434             default: {
435                 LogMsg(LOGERROR, NONFATALERROR, "Illegal path list.");
436             }
437         }
438 #if WRTABS_COMMENT
439         WriteString(" %% ");
440         WRTNUM(e->count)
441         switch (e->type) {
442             case CURVETO:
443                 wrtfx(c1.x);
444                 wrtfx(c1.y);
445                 wrtfx(c2.x);
446                 wrtfx(c2.y);
447                 wrtfx(c3.x);
448                 wrtfx(c3.y);
449                 WriteString("ct");
450                 break;
451             case LINETO:
452                 wrtfx(c1.x);
453                 wrtfx(c1.y);
454                 WriteString("dt");
455                 break;
456             case MOVETO:
457                 wrtfx(c1.x);
458                 wrtfx(c1.y);
459                 WriteString("mt");
460                 break;
461             case CLOSEPATH:
462                 WriteString("cp");
463                 break;
464         }
465         WriteString("\n");
466 #endif
467         e = e->next;
468     }
469     WriteString("ed\n");
470 }
471