xref: /potrace-1.14/src/backend_eps.c (revision b3fce824)
1 /* Copyright (C) 2001-2017 Peter Selinger.
2    This file is part of Potrace. It is free software and it is covered
3    by the GNU General Public License. See the file COPYING for details. */
4 
5 
6 /* The Postscript backend of Potrace. This can produce "ps" or "eps"
7    output, and different kinds of graphical debugging
8    output. Postscript compression is optionally supplied via the
9    functions in flate.c. */
10 
11 #define _XOPEN_SOURCE 500
12 
13 #ifdef HAVE_CONFIG_H
14 #include <config.h>
15 #endif
16 
17 #include <stdio.h>
18 #include <stdarg.h>
19 #include <string.h>
20 #include <math.h>
21 #include <stdlib.h>
22 
23 #include "potracelib.h"
24 #include "curve.h"
25 #include "main.h"
26 #include "backend_eps.h"
27 #include "flate.h"
28 #include "lists.h"
29 #include "auxiliary.h"
30 
31 #define SAFE_CALLOC(var, n, typ) \
32   if ((var = (typ *)calloc(n, sizeof(typ))) == NULL) goto calloc_error
33 
34 typedef int color_t;
35 
36 #define black  0x000000
37 #define red    0xff0000
38 #define green  0x008000
39 #define blue   0x0000ff
40 
41 #define TRY(x) if (x) goto try_error
42 
43 /* ---------------------------------------------------------------------- */
44 /* functions for interfacing with compression backend */
45 
46 /* xship: callback function that must be initialized before calling
47    any other functions of the "ship" family. xship_file must be
48    initialized too. */
49 
50 /* print the token to f, but filtered through a compression
51    filter in case filter!=0 */
52 static int (*xship)(FILE *f, int filter, const char *s, int len);
53 static FILE *xship_file;
54 
55 /* ship postscript code, filtered */
ship(const char * fmt,...)56 static int ship(const char *fmt, ...) {
57   va_list args;
58   static char buf[4096]; /* static string limit is okay here because
59 			    we only use constant format strings - for
60 			    the same reason, it is okay to use
61 			    vsprintf instead of vsnprintf below. */
62   va_start(args, fmt);
63   vsprintf(buf, fmt, args);
64   buf[4095] = 0;
65   va_end(args);
66 
67   xship(xship_file, 1, buf, strlen(buf));
68   return 0;
69 }
70 
71 /* ship a postscript comment, unfiltered */
shipcom(const char * fmt,...)72 static int shipcom(const char *fmt, ...) {
73   static char buf[4096];
74   va_list args;
75 
76   va_start(args, fmt);
77   vsprintf(buf, fmt, args);
78   buf[4095] = 0;
79   va_end(args);
80 
81   xship(xship_file, 0, buf, strlen(buf));
82   return 0;
83 }
84 
85 /* set all callback functions */
eps_callbacks(FILE * fout)86 static void eps_callbacks(FILE *fout) {
87   if (info.compress && info.pslevel==2) {
88     xship = lzw_xship;
89   } else if (info.compress && info.pslevel==3) {
90     xship = flate_xship;
91   } else {
92     xship = dummy_xship;
93   }
94   xship_file = fout;
95 }
96 
97 /* ---------------------------------------------------------------------- */
98 /* postscript path-drawing auxiliary functions */
99 
100 /* coordinate quantization */
unit(dpoint_t p)101 static inline point_t unit(dpoint_t p) {
102   point_t q;
103 
104   q.x = (long)(floor(p.x*info.unit+.5));
105   q.y = (long)(floor(p.y*info.unit+.5));
106   return q;
107 }
108 
109 /* current point */
110 static point_t cur;
111 
eps_coords(dpoint_t p)112 static void eps_coords(dpoint_t p) {
113   cur = unit(p);
114   ship("%ld %ld ", cur.x, cur.y);
115 }
116 
eps_rcoords(dpoint_t p)117 static void eps_rcoords(dpoint_t p) {
118   point_t q;
119 
120   q = unit(p);
121   ship("%ld %ld ", q.x-cur.x, q.y-cur.y);
122   cur = q;
123 }
124 
eps_moveto(dpoint_t p)125 static void eps_moveto(dpoint_t p) {
126   eps_coords(p);
127   ship("moveto\n");
128 }
129 
130 /* move to point + offset */
eps_moveto_offs(dpoint_t p,double xoffs,double yoffs)131 static void eps_moveto_offs(dpoint_t p, double xoffs, double yoffs) {
132   /* note: structs are passed by value, so the following assignment
133      does not modify the original struct in the caller */
134   p.x += xoffs;
135   p.y += yoffs;
136   eps_coords(p);
137   ship("moveto\n");
138 }
139 
eps_lineto(dpoint_t p)140 static void eps_lineto(dpoint_t p) {
141   eps_rcoords(p);
142   ship("rlineto\n");
143 }
144 
eps_curveto(dpoint_t p1,dpoint_t p2,dpoint_t p3)145 static void eps_curveto(dpoint_t p1, dpoint_t p2, dpoint_t p3) {
146   point_t q1, q2, q3;
147 
148   q1 = unit(p1);
149   q2 = unit(p2);
150   q3 = unit(p3);
151 
152   ship("%ld %ld %ld %ld %ld %ld rcurveto\n", q1.x-cur.x, q1.y-cur.y, q2.x-cur.x, q2.y-cur.y, q3.x-cur.x, q3.y-cur.y);
153 
154   cur = q3;
155 }
156 
157 /* this procedure returns a statically allocated string */
eps_colorstring(const color_t col)158 static const char *eps_colorstring(const color_t col) {
159   double r, g, b;
160   static char buf[100];
161 
162   r = (col & 0xff0000) >> 16;
163   g = (col & 0x00ff00) >> 8;
164   b = (col & 0x0000ff) >> 0;
165 
166   if (r==0 && g==0 && b==0) {
167     return "0 setgray";
168   } else if (r==255 && g==255 && b==255) {
169     return "1 setgray";
170   } else if (r == g && g == b) {
171     sprintf(buf, "%.3f setgray", r/255.0);
172     return buf;
173   } else {
174     sprintf(buf, "%.3f %.3f %.3f setrgbcolor", r/255.0, g/255.0, b/255.0);
175     return buf;
176   }
177 }
178 
179 static color_t eps_color = -1;
180 static double eps_width = -1;
181 
eps_setcolor(const color_t col)182 static void eps_setcolor(const color_t col) {
183   if (col == eps_color) {
184     return;
185   }
186   eps_color = col;
187 
188   ship("%s\n", eps_colorstring(col));
189 }
190 
eps_linewidth(double w)191 static void eps_linewidth(double w) {
192   if (w == eps_width) {
193     return;
194   }
195   eps_width = w;
196   ship("%f setlinewidth\n", w * info.unit);
197 }
198 
199 /* ---------------------------------------------------------------------- */
200 /* functions for converting a path to postscript code */
201 
202 /* ---------------------------------------------------------------------- */
203 /* ASCII encoding */
204 
205 /* explicit encoding, does not use special macros */
eps_path_long(privcurve_t * curve)206 static int eps_path_long(privcurve_t *curve) {
207   int i;
208   dpoint_t *c;
209   int m = curve->n;
210 
211   c = curve->c[m-1];
212   eps_moveto(c[2]);
213 
214   for (i=0; i<m; i++) {
215     c = curve->c[i];
216     switch (curve->tag[i]) {
217     case POTRACE_CORNER:
218       eps_lineto(c[1]);
219       eps_lineto(c[2]);
220       break;
221     case POTRACE_CURVETO:
222       eps_curveto(c[0], c[1], c[2]);
223       break;
224     }
225   }
226   return 0;
227 }
228 
229 /* size-optimized encoding relies on special macros */
eps_path_short(privcurve_t * curve)230 static int eps_path_short(privcurve_t *curve) {
231   int i, i1;
232   long int *bq = NULL;  /* bq[m] */
233   long int *aq = NULL;  /* aq[m] */
234   point_t *v = NULL;    /* v[m] */
235   dpoint_t *q = NULL;   /* q[m] */
236   double M;
237   int m = curve->n;
238 
239   SAFE_CALLOC(bq, m, long int);
240   SAFE_CALLOC(aq, m, long int);
241   SAFE_CALLOC(v, m, point_t);
242   SAFE_CALLOC(q, m, dpoint_t);
243 
244   /* quantize vertices */
245   for (i=0; i<m; i++) {
246     v[i] = unit(curve->vertex[i]);
247   }
248 
249   /* quantize beta */
250   for (i=0; i<m; i++) {
251     i1 = mod(i+1,m);
252     M = max(10, max(abs(v[i1].x-v[i].x), abs(v[i1].y-v[i].y)));
253     bq[i] = (int)(M * curve->beta[i] + 0.5);
254     if (curve->beta[i] != 0.5) {
255       q[i1] = interval(bq[i]/M, dpoint(v[i]), dpoint(v[i1]));
256     } else {
257       q[i1] = interval(0.5, dpoint(v[i]), dpoint(v[i1]));
258     }
259   }
260 
261   /* quantize alpha */
262   for (i=0; i<m; i++) {
263     i1 = mod(i+1,m);
264     M = max(10, max(max(abs(q[i].x-v[i].x), abs(q[i].y-v[i].y)),
265 		    max(abs(v[i].x-q[i1].x), abs(v[i].y-q[i1].y))));
266     if (curve->tag[i] == POTRACE_CURVETO) {
267       aq[i] = (int)(M * curve->alpha[i] + 0.5);
268       if (aq[i] > M) {
269 	aq[i]--;
270       }
271     }
272   }
273 
274   /* generate output */
275   ship("%ld %ld ", v[m-1].x, v[m-1].y);
276   ship("%ld %ld ", v[0].x - v[m-1].x, v[0].y - v[m-1].y);
277   if (curve->beta[m-1] == 0.5) {
278     ship("i\n");
279   } else {
280     ship("%ld I\n", bq[m-1]);
281   }
282   for (i=0; i<m; i++) {
283     if (i<m-1) {
284       ship("%ld %ld ", v[i+1].x - v[i].x, v[i+1].y - v[i].y);
285       if (curve->beta[i] != 0.5) {
286 	ship("%ld ", bq[i]);
287       }
288     }
289     if (curve->tag[i] == POTRACE_CURVETO) {
290       ship(curve->beta[i] == 0.5 ? "%ld c\n" : "%ld C\n", aq[i]);
291     } else {
292       ship(curve->beta[i] == 0.5 ? "v\n" : "V\n");
293     }
294   }
295 
296   free(bq);
297   free(aq);
298   free(v);
299   free(q);
300   return 0;
301 
302  calloc_error:
303   free(bq);
304   free(aq);
305   free(v);
306   free(q);
307   return 1;
308 }
309 
eps_path(privcurve_t * curve)310 static int eps_path(privcurve_t *curve) {
311   if (info.longcoding==0 && curve->alphacurve) {
312     return eps_path_short(curve);
313   } else {
314     return eps_path_long(curve);
315   }
316 }
317 
318 /* ---------------------------------------------------------------------- */
319 /* functions for rendering various internal data structures, used to
320    generate debugging output */
321 
322 /* output jaggie curve in grey */
eps_jaggy(potrace_path_t * plist)323 static void eps_jaggy(potrace_path_t *plist) {
324   potrace_path_t *p;
325   int i;
326 
327   ship(".9 setgray\n");
328   list_forall (p, plist) {
329     point_t *pt = p->priv->pt;
330     point_t cur, prev;
331 
332     if (p->sign == '+') {
333       cur = prev = pt[p->priv->len-1];
334       eps_moveto(dpoint(cur));
335       for (i=0; i<p->priv->len; i++) {
336 	if (pt[i].x != cur.x && pt[i].y != cur.y) {
337 	  cur = prev;
338 	  eps_lineto(dpoint(cur));
339 	}
340 	prev = pt[i];
341       }
342       eps_lineto(dpoint(pt[p->priv->len-1]));
343     } else {
344       cur = prev = pt[0];
345       eps_moveto(dpoint(cur));
346       for (i=p->priv->len-1; i>=0; i--) {
347 	if (pt[i].x != cur.x && pt[i].y != cur.y) {
348           cur = prev;
349           eps_lineto(dpoint(cur));
350         }
351         prev = pt[i];
352       }
353       eps_lineto(dpoint(pt[0]));
354     }
355     if (p->next == NULL || p->next->sign == '+') {
356       ship("fill\n");
357     }
358   }
359 }
360 
361 /* output polygon */
eps_polygon(privcurve_t * curve,const color_t col)362 static void eps_polygon(privcurve_t *curve, const color_t col) {
363   int i;
364   int m = curve->n;
365 
366   eps_linewidth(.02);
367   eps_setcolor(col);
368   eps_moveto(curve->vertex[m-1]);
369   for (i=0; i<m; i++) {
370     eps_lineto(curve->vertex[i]);
371   }
372   ship("stroke\n");
373 }
374 
375 /* output lines L and parameter alpha */
eps_L(privcurve_t * curve,const color_t col)376 static void eps_L(privcurve_t *curve, const color_t col) {
377   int i, i1;
378   double gamma;
379   dpoint_t p1, p4, p1l, p4l;
380   int m = curve->n;
381 
382   for (i=0; i<m; i++) {
383     i1 = mod(i+1, m);
384     gamma = curve->alpha0[i1] * 0.75;
385 
386     p1 = curve->c[i][2];
387     p4 = curve->c[i1][2];
388     p1l = interval(gamma, p1, curve->vertex[i1]);
389     p4l = interval(gamma, p4, curve->vertex[i1]);
390     eps_linewidth(.02);
391     eps_setcolor(col);
392     eps_moveto(p1l);
393     eps_lineto(p4l);
394     ship("stroke\n");
395     eps_moveto_offs(curve->vertex[i1], -.4, -.4);
396     ship("times (%.2f) show\n", curve->alpha0[i1]);
397   }
398 }
399 
400 /* ---------------------------------------------------------------------- */
401 /* postscript macros */
402 
403 /* special macros for size-optimized rendering of Bezier curves */
404 static const char *optimacros =
405   "/D{bind def}def\n"
406   "/R{roll}D\n"
407   "/K{copy}D\n"
408   "/P{pop}D\n"
409   "/p{3 2 R add 3 1 R add exch}D\n"
410   "/t{dup 4 3 R mul 3 1 R mul}D\n"
411   "/a{dup 1 sub neg 4 1 R t 5 2 R t p}D\n"
412   "/m{2 K le{exch}if P}D\n"
413   "/n{abs exch abs m}D\n"
414   "/d{-1 t p n}D\n"
415   "/s{[4 2 R] cvx def}D\n"
416   "/g{7 K P 4 K P P d 5 1 R d 10 m m div 5 K 12 8 R 5 4 R a 9 4 R 3 2 R a 6 4 R curveto}D\n"
417   "/e{4 2 R lineto lineto P P}D\n"
418   "/q{3 K P n 10 m div}D\n"
419   "/f{x y 7 4 R 5 1 R 4 K p /y s 7 2 R 2 K 9 7 R 7 6 R t p 2 K /x s}D\n"
420   "/C{4 1 R q f 7 6 R g}D\n"
421   "/V{q f e}D\n"
422   "/c{3 1 R .5 f 7 6 R g}D\n"
423   "/v{.5 f e}D\n"
424   "/j{5 K P p /y s 3 K t 7 5 R p /x s x moveto P}D\n"
425   "/i{.5 j}D\n"
426   "/I{dup 6 1 R q j 3 2 R}D\n"
427   "/z{closepath}D\n"
428   "/b{%s z fill}D\n"
429   "/w{%s z fill}D\n";
430 
431 /* special macros for debug output */
432 static const char *debugmacros =
433   "/unit { %f } def\n"
434   "/box { newpath 0 0 moveto 0 1 lineto 1 1 lineto 1 0 lineto closepath } def\n"
435   "/circ { newpath 0 0 1 0 360 arc closepath } def\n"
436   "/dot { gsave .15 mul dup scale circ fill grestore } def\n"
437   "/sq { gsave unit unit scale -.5 -.5 translate box .02 setlinewidth stroke grestore } def\n"
438   "/sq1 { gsave translate sq unit .6 mul dot grestore } def\n"
439   "/dot2 { gsave translate unit dot grestore } def\n"
440   "/usq { gsave unit unit scale -.5 -.5 rmoveto 0 1 rlineto 1 0 rlineto 0 -1 rlineto closepath .02 setlinewidth stroke grestore } def\n"
441   "/dot1 { gsave translate unit .3 mul dup scale circ fill grestore } def\n"
442   "/times { /Times-Roman findfont unit .3 mul scalefont setfont } def\n"
443   "/times1 { /Times-Roman findfont unit 10 mul scalefont setfont 0 0 0 setrgbcolor } def\n"
444   "/times2 { /Times-Roman findfont unit 2 mul scalefont setfont 0 0 0 setrgbcolor } def\n";
445 
446 /* ---------------------------------------------------------------------- */
447 /* Backends for various types of output. */
448 
449 /* Normal output: black on transparent */
render0(potrace_path_t * plist)450 static int render0(potrace_path_t *plist) {
451   potrace_path_t *p;
452 
453   if (info.longcoding) {
454     eps_setcolor(info.color);
455     list_forall (p, plist) {
456       TRY(eps_path(p->priv->fcurve));
457       ship("closepath\n");
458       if (p->next == NULL || p->next->sign == '+') {
459 	ship("fill\n");
460       }
461     }
462   } else {
463     list_forall (p, plist) {
464       TRY(eps_path(p->priv->fcurve));
465       if (p->next == NULL || p->next->sign == '+') {
466 	ship("b\n");
467       } else {
468 	ship("z\n");
469       }
470     }
471   }
472   return 0;
473 
474  try_error:
475   return 1;
476 }
477 
478 /* Opaque output: alternating black and white */
render0_opaque(potrace_path_t * plist)479 static int render0_opaque(potrace_path_t *plist) {
480   potrace_path_t *p;
481 
482   if (info.longcoding) {
483     list_forall (p, plist) {
484       TRY(eps_path(p->priv->fcurve));
485       ship("closepath\n");
486       eps_setcolor(p->sign=='+' ? info.color : info.fillcolor);
487       ship("fill\n");
488     }
489   } else {
490     list_forall (p, plist) {
491       TRY(eps_path(p->priv->fcurve));
492       ship(p->sign=='+' ? "b\n" : "w\n");
493     }
494   }
495   return 0;
496 
497  try_error:
498   return 1;
499 }
500 
501 /* Debug output type 1 (show optimal polygon) */
render1(potrace_path_t * plist)502 static int render1(potrace_path_t *plist) {
503   potrace_path_t *p;
504   int i;
505 
506   eps_jaggy(plist);
507 
508   list_forall (p, plist) {
509 
510     point_t *pt = p->priv->pt;
511     int n = p->priv->len;
512     int m = p->priv->m;
513     int *po = p->priv->po;
514 
515     eps_linewidth(.02);
516     eps_setcolor(black);
517     /* output jaggie curve in boxed style */
518     for (i=1; i<n; i++) {
519       eps_moveto(dpoint(pt[i-1]));
520       eps_lineto(dpoint(pt[i]));
521       ship("stroke\n");
522       eps_coords(dpoint(pt[i]));
523       ship("sq1\n");
524     }
525     eps_moveto(dpoint(pt[n-1]));
526     eps_lineto(dpoint(pt[0]));
527     ship("stroke\n");
528     eps_coords(dpoint(pt[0]));
529     ship("sq1\n");
530 
531     /* output the uncorrected polygon */
532     eps_linewidth(.1);
533     eps_setcolor(blue);
534     eps_moveto(dpoint(pt[po[0]]));
535     for (i=1; i<m; i++) {
536       eps_lineto(dpoint(pt[po[i]]));
537     }
538     eps_lineto(dpoint(pt[po[0]]));
539     ship("stroke\n");
540     for (i=0; i<m; i++) {
541       eps_coords(dpoint(pt[po[i]]));
542       ship("dot2\n");
543     }
544   }
545   return 0;
546 }
547 
548 /* Debug output type 2 (show corrected polygon and edge detection) */
render2(potrace_path_t * plist)549 static int render2(potrace_path_t *plist) {
550   potrace_path_t *p;
551   int i;
552 
553   /* output original bitmap in grey */
554   eps_jaggy(plist);
555 
556   list_forall (p, plist) {
557     /* output polygon with corrected edges, lines L, and parameter alpha */
558     eps_polygon(&p->priv->curve, black);
559     eps_L(&p->priv->curve, black);
560 
561     /* output the vertex unit squares */
562     for (i=0; i<p->priv->curve.n; i++) {
563       eps_moveto(p->priv->curve.vertex[i]);
564       ship("usq\n");
565     }
566 
567     /* output the path */
568     eps_linewidth(.1);
569     eps_setcolor(blue);
570     TRY(eps_path(&p->priv->curve));
571     ship("closepath\n");
572     ship("stroke\n");
573 
574     if (info.param->opticurve && info.debug == 3) {
575 
576       /* output opticurve */
577       eps_linewidth(.05);
578       eps_setcolor(red);
579       TRY(eps_path(&p->priv->ocurve));
580       ship("closepath\n");
581       ship("stroke\n");
582 
583       /* output dots */
584       for (i=0; i<p->priv->ocurve.n; i++) {
585 	eps_coords(p->priv->ocurve.c[i][2]);
586 	ship("dot1\n");
587       }
588     }
589   }
590   return 0;
591 
592  try_error:
593   return 1;
594 }
595 
596 /* Free-style debug output */
render_debug(potrace_path_t * plist)597 static int render_debug(potrace_path_t *plist) {
598   potrace_path_t *p;
599   int count;
600   int i;
601 
602   /* output original bitmap in grey */
603   eps_jaggy(plist);
604 
605   count = -1;
606   list_forall (p, plist) {
607     count++;
608 
609     /* output path numbers */
610     eps_moveto_offs(p->priv->curve.vertex[0], 0, 5);
611     ship("times1 (%d) show\n", count);
612 
613     /* output polygon with corrected edges, lines L, and parameter alpha */
614     eps_polygon(&p->priv->curve, black);
615     eps_L(&p->priv->curve, black);
616 
617     /* output the vertex unit squares */
618     for (i=0; i<p->priv->curve.n; i++) {
619       eps_moveto(p->priv->curve.vertex[i]);
620       ship("usq\n");
621     }
622 
623     /* output the vertex numbers */
624     for (i=0; i<p->priv->curve.n; i++) {
625       eps_moveto_offs(p->priv->curve.vertex[i], +1, +1);
626       ship("times2 (%d) show\n", i);
627     }
628 
629     /* output the path */
630     eps_linewidth(.1);
631     eps_setcolor(blue);
632     TRY(eps_path(&p->priv->curve));
633     ship("closepath\n");
634     ship("stroke\n");
635 
636     if (info.param->opticurve) {
637 
638       /* output the opti-verteces polygon */
639       eps_polygon(&p->priv->ocurve, green);
640 
641       /* output opticurve */
642       eps_linewidth(.05);
643       eps_setcolor(red);
644       TRY(eps_path(&p->priv->ocurve));
645       ship("closepath\n");
646       ship("stroke\n");
647 
648       /* output dots */
649       for (i=0; i<p->priv->ocurve.n; i++) {
650 	eps_coords(p->priv->ocurve.c[i][2]);
651 	ship("dot1\n");
652       }
653 
654       /* output beta parameters */
655       for (i=0; i<p->priv->ocurve.n; i++) {
656 	eps_moveto_offs(p->priv->ocurve.c[i][2], +.4, -.4);
657 	ship("times (%.2f) show\n", p->priv->ocurve.beta[i]);
658       }
659     }
660   }
661   return 0;
662 
663  try_error:
664   return 1;
665 }
666 
667 /* select the appropriate rendering function from above */
eps_render(potrace_path_t * plist)668 static int eps_render(potrace_path_t *plist) {
669   int r;
670 
671   switch (info.debug) {
672   case 0:
673     if (info.opaque) {
674       r = render0_opaque(plist);
675     } else {
676       r = render0(plist);
677     }
678     break;
679   case 1:
680     r = render1(plist);
681     break;
682   case 2: case 3:
683     r = render2(plist);
684     break;
685   default:
686     r = render_debug(plist);
687     break;
688   }
689   return r;
690 }
691 
692 /* ---------------------------------------------------------------------- */
693 /* EPS header and footer */
694 
eps_init(imginfo_t * imginfo)695 static int eps_init(imginfo_t *imginfo) {
696   double origx = imginfo->trans.orig[0] + imginfo->lmar;
697   double origy = imginfo->trans.orig[1] + imginfo->bmar;
698   double scalex = imginfo->trans.scalex / info.unit;
699   double scaley = imginfo->trans.scaley / info.unit;
700   char *c0, *c1;
701 
702   shipcom("%%!PS-Adobe-3.0 EPSF-3.0\n");
703   shipcom("%%%%Creator: " POTRACE " " VERSION ", written by Peter Selinger 2001-2017\n");
704   shipcom("%%%%LanguageLevel: %d\n", info.pslevel);
705   shipcom("%%%%BoundingBox: 0 0 %.0f %.0f\n",
706 	  ceil(imginfo->trans.bb[0]+imginfo->lmar+imginfo->rmar),
707 	  ceil(imginfo->trans.bb[1]+imginfo->tmar+imginfo->bmar));
708   shipcom("%%%%HiResBoundingBox: 0 0 %f %f\n",
709 	  imginfo->trans.bb[0]+imginfo->lmar+imginfo->rmar,
710 	  imginfo->trans.bb[1]+imginfo->tmar+imginfo->bmar);
711   shipcom("%%%%Pages: 1\n");
712   shipcom("%%%%EndComments\n");
713 
714   shipcom("%%%%Page: 1 1\n");
715   ship("save\n");
716   if (!info.longcoding) {
717     c0 = strdup(eps_colorstring(info.color));
718     c1 = strdup(eps_colorstring(info.fillcolor));
719     if (!c0 || !c1) {
720       free(c0);
721       free(c1);
722       return 1;
723     }
724     ship(optimacros, c0, c1);
725     free(c0);
726     free(c1);
727   }
728   if (info.debug) {
729     ship(debugmacros, info.unit);
730   }
731   if (origx != 0 || origy != 0) {
732     ship("%f %f translate\n", origx, origy);
733   }
734   if (info.angle != 0) {
735     ship("%.2f rotate\n", info.angle);
736   }
737   ship("%f %f scale\n", scalex, scaley);
738 
739   return 0;
740 }
741 
eps_term(void)742 static void eps_term(void) {
743   ship("restore\n");
744   shipcom("%%%%EOF\n");
745 }
746 
747 /* public interface for EPS */
page_eps(FILE * fout,potrace_path_t * plist,imginfo_t * imginfo)748 int page_eps(FILE *fout, potrace_path_t *plist, imginfo_t *imginfo) {
749   eps_callbacks(fout);
750 
751   TRY(eps_init(imginfo));
752   TRY(eps_render(plist));
753   eps_term();
754   return 0;
755 
756  try_error:
757   return 1;
758 }
759 
760 /* ---------------------------------------------------------------------- */
761 /* PostScript header and footer */
762 
763 static int eps_pagenumber;
764 
init_ps(FILE * fout)765 int init_ps(FILE *fout) {
766   char *c0, *c1;
767 
768   /* set callback functions for shipping routines */
769   eps_callbacks(fout);
770 
771   shipcom("%%!PS-Adobe-3.0\n");
772   shipcom("%%%%Creator: " POTRACE " " VERSION ", written by Peter Selinger 2001-2017\n");
773   shipcom("%%%%LanguageLevel: %d\n", info.pslevel);
774   shipcom("%%%%BoundingBox: 0 0 %d %d\n", info.paperwidth, info.paperheight);
775   shipcom("%%%%Pages: (atend)\n");
776   shipcom("%%%%EndComments\n");
777   if (!info.longcoding || info.debug) {
778     shipcom("%%%%BeginSetup\n");
779     if (!info.longcoding) {
780       c0 = strdup(eps_colorstring(info.color));
781       c1 = strdup(eps_colorstring(info.fillcolor));
782       if (!c0 || !c1) {
783 	free(c0);
784 	free(c1);
785 	return 1;
786       }
787       ship(optimacros, c0, c1);
788       free(c0);
789       free(c1);
790     }
791     if (info.debug) {
792       ship(debugmacros, info.unit);
793     }
794     shipcom("%%%%EndSetup\n");
795   }
796   eps_pagenumber = 0;
797   fflush(fout);
798   return 0;
799 }
800 
term_ps(FILE * fout)801 int term_ps(FILE *fout) {
802   eps_callbacks(fout);
803 
804   shipcom("%%%%Trailer\n");
805   shipcom("%%%%Pages: %d\n", eps_pagenumber);
806   shipcom("%%%%EOF\n");
807   fflush(fout);
808 
809   return 0;
810 }
811 
eps_pageinit_ps(imginfo_t * imginfo)812 static void eps_pageinit_ps(imginfo_t *imginfo) {
813   double origx = imginfo->trans.orig[0] + imginfo->lmar;
814   double origy = imginfo->trans.orig[1] + imginfo->bmar;
815   double scalex = imginfo->trans.scalex / info.unit;
816   double scaley = imginfo->trans.scaley / info.unit;
817 
818   eps_pagenumber++;
819   eps_color = -1;
820   eps_width = -1;
821 
822   shipcom("%%%%Page: %d %d\n", eps_pagenumber, eps_pagenumber);
823   ship("save\n");
824   if (origx != 0 || origy != 0) {
825     ship("%f %f translate\n", origx, origy);
826   }
827   if (info.angle != 0) {
828     ship("%.2f rotate\n", info.angle);
829   }
830   ship("%f %f scale\n", scalex, scaley);
831 }
832 
eps_pageterm_ps(void)833 static void eps_pageterm_ps(void) {
834   ship("restore\n");
835   ship("showpage\n");
836 }
837 
page_ps(FILE * fout,potrace_path_t * plist,imginfo_t * imginfo)838 int page_ps(FILE *fout, potrace_path_t *plist, imginfo_t *imginfo) {
839   int r;
840 
841   eps_callbacks(fout);
842 
843   eps_pageinit_ps(imginfo);
844 
845   r = eps_render(plist);
846   if (r) {
847     return r;
848   }
849 
850   eps_pageterm_ps();
851 
852   shipcom("");
853 
854   fflush(fout);
855 
856   return 0;
857 }
858