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